一、前言

      多线程操作一直是编程的常用操作,掌握好基本的操作可以让程序运行的更加有效。本文不求大而全,只是将我自己工作中常常用到的多线程操作做个分类和总结。平时记性不好的时候还能看看。本文参考了多篇园子里的精彩博文,在文章最后会贴出具体来源,感谢他们的无私奉献。

二、关于线程

(1) 为何使用线程:

可以使用线程将代码同其他代码隔离,提高应用程序的可靠性;

可以使用线程来简化编码;

可以使用线程来实现并发执行。

(2) 进程、应用程序域以及线程的关系:

进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。

应用程序域(AppDomain)是一个程序运行的逻辑区域,它可以视为一个轻量级的进程,.NET的程序集正是在应用程序域中运行的,一个进程可以包含有多个应用程序域,一个应用程序域也可以包含多个程序集。在一个应用程序域中包含了一个或多个上下文context,使用上下文CLR就能够把某些特殊对象的状态放置在不同容器当中。

线程(Thread)是进程中的基本执行单元,在进程入口执行的第一个线程被视为这个进程的主线程。

关系图如下:

三、Thread

      Thread可能是除了Task之外用的最多的多线程类。一般用法:

// one thread
Thread thread = new ThreadStart(functiion);
thread.Start(); // thread.join
Thread ThreadA = new Thread(delegate()
{
  //do something      
});
  
Thread ThreadB = new Thread(delegate()
{       
  //do something;  
  
ThreadA.Join();
  
  //do another thing      
});
  
//启动线程
ThreadA.Start();
ThreadB.Start(); //一开始,两个线程相互交替运行,当线程B运行到线程A的join时,会先让线程A执行完,然后线程B再继续执行。你可以理解为超车,一开始两者互不相让,当join时,线程A超车了线程B

四、ThreadPool

由于线程的创建和销毁需要耗费一定的开销,过多的使用线程会造成内存资源的浪费,出于对性能的考虑,于是引入了线程池的概念。线程池维护一个请求队列,线程池的代码从队列提取任务,然后委派给线程池的一个线程执行,线程执行完不会被立即销毁,这样既可以在后台执行任务,又可以减少线程创建和销毁所带来的开销。

如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。

ThreadPool.QueueUserWorkItem(function,parameter);

五、Task

      Task是我用的最多的多线程方式,一般使用的方法如下:

// one task
var task = new Task(() =>{
// do something
});
task.Start();

// task one by one
var task = new Task(() =>{
 // do something  
}); Task task2 = task.ContinueWith(()=>{
// do something  
});
task.Start();
// many tasks
var tasks = new Task[PackCount]; //多线程任务 for (int index = ; index < PackCount; index++)
{
int Threadindex = index;
var task = new Task(() =>
{
// do something }
});
tasks[Threadindex] = task;
task.Start();
}
Task.WaitAll(tasks); //等待所有线程完成
//Task.WaitAny(tasks); //等待一个线程完成继续执行主程序 //Task.Factory
Task.Factory.StartNew(()=>{ // do something });

六、Invoke、BeginInvoke、DynamicInvoke

此处主要说明的是delegate下的各种Invoke

Invoke (委托方法执行在调用处同一个线程中

delegate void MyDelegate();

MyDelegate del = new MyDelegate(Function);
del .Invoke(); //使用到委托的invoke方法

BeginInvoke(它从线程池中抓取一个空闲线程,来委托执行方法)

A情况:使用IAsyncResult.IsCompleted判断子线程是否执行完毕

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,null,null);

//if the branch thread is not completed
while(!result.IsCompleted)
{
// the main thread do another thing
} T data = del.EndInvoke(result);
// var data is the result of Function with parameter

B情况:使用IAsyncResult.AsyncWaitHandle.WaitOne(timeout)判断子线程是否执行完毕

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,null,null);

//if the branch thread is not completed
while(!result.AsyncWaitHandle.WaitOne(int timeout))
{
// the main thread do another thing
} T data = del.EndInvoke(result);
// var data is the result of Function with parameter

C情况:使用WaitHandle.WaitAll(WaitHandle[],timeout)判断子线程是否执行完毕

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,null,null);

WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle,........ };

while (!WaitHandle.WaitAll(waitHandleList,int timeout))
{
// the main thread do another thing
} T data = del.EndInvoke(result);
// var data is the result of Function with parameter

D情况:使用轮询方式来检测异步方法的状态非常麻烦,而且效率不高,为此需要使用回调函数。主线程可以安心做自己的事,而异步线程完成操作后执行回调函数即可。回调函数依然是在异步线程上,而非主线程上。

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,new AsyncCallback(callbackFunction),object);

....MainThread do somethng...

static void callbackFunction(IAsyncResult result)
{
AsyncResult _result = (AsyncResult )result; MyDelegate del = (MyDelegate)_result.AsyncDelegate; T data = del.EndInvoke(_result); T1 objectReciever = (T1)result.AsyncResult; //object=result.AsyncResult
}

        DynamicInvoke:

       与Delegate.Invoke类似,同步,且同线程,唯一不同的是,是采用后期绑定的方式来调用委托方法,所以时间代价较大。

工作实例:在WPF中出现一种异常:“调用线程无法访问此对象,因为另一个线程拥有该对象。

情况A:假设发生该异常的代码是在xaml.cs文件中,那么Dispatcher.Invoke已经够用了。

情况B: 假设发生该异常的代码是在.cs文件中,那么在Stack Overflow上有一招:

 private void RaiseEventOnUIThread(Delegate theEvent, object[] args)
{
foreach (Delegate d in theEvent.GetInvocationList())
{
ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
if (syncer == null) //静态函数为null
{
d.DynamicInvoke(args);
}
else
{
syncer.BeginInvoke(d, args); //在创建了此对象的线程上异步执行委托
}
}
}

References:

【1】http://blog.sina.com.cn/s/blog_5a6f39cf0100qtzf.html

【2】http://www.cnblogs.com/slikyn/articles/1525940.html

【3】http://kb.cnblogs.com/page/130487/#t3

【4】http://blog.csdn.net/soft_123456/article/details/38819877

【5】http://www.cnblogs.com/laoyur/archive/2011/04/14/2016025.html

【6】http://blog.csdn.net/cselmu9/article/details/8274556

【7】http://stackoverflow.com/questions/1698889/raise-events-in-net-on-the-main-ui-thread

C#中级-常用多线程操作(持续更新)的更多相关文章

  1. git常用命令(持续更新中)

    git常用命令(持续更新中) 本地仓库操作git int                                 初始化本地仓库git add .                       ...

  2. 【github&&git】4、git常用命令(持续更新中)

    git常用命令(持续更新中) 本地仓库操作git int                                 初始化本地仓库git add .                       ...

  3. 总结js常用函数和常用技巧(持续更新)

    学习和工作的过程中总结的干货,包括常用函数.常用js技巧.常用正则表达式.git笔记等.为刚接触前端的童鞋们提供一个简单的查询的途径,也以此来缅怀我的前端学习之路. PS:此文档,我会持续更新. Aj ...

  4. 【笔记】git 的常用操作命令(持续更新。。。)

    项目正在如火如荼的开展,代码量的繁多不得不令我们运用 git 这个有用的工具去管理我们共同协作的代码 git 在这里不作什么介绍了,百度一大堆的教程 首推廖雪峰老师的:http://www.liaox ...

  5. php常用函数(持续更新)

    每一种编程语言在用的过程中都会发现有时候要一种特定需求的功能函数,结果没有内置这样的函数,这个时候就需要自己根据已有函数编写尽可能简单的函数,下面是我在做php相关工作时积累下的函数,会持续更新,您要 ...

  6. js 常用操作 -- 持续更新

    替换数组中某一元素: array.splice(2, 1, '哈哈'); // 2 表示指定数组中2下标元素,1表示要删除的项数,哈哈 是替换后的值 在数组中某元素之前增加元素: array.spli ...

  7. jgGrid常用操作--持续更新

    最近有使用到jqGrid框架,有个需求是单击某个字段,比如name,然后把id带过去执行一个function,网上有说用线获取选中行,然后再得到id的方法,此方法经实验,必须要先选中才行,在用户没有进 ...

  8. Jenkins常用插件说明(持续更新)

    本文主要记录在学习以及使用Jenkins过程中常用的对我们有帮助的插件,同时本文将会持续进行更新.如果大家发现有其他野很好用的插件,也欢迎参照下面的格式,在评论中进行回复反馈. 一.通用插件 1.Em ...

  9. Word, PPT和Excel的常用技巧(持续更新)

    本文的目的是记录平时使用Word, PowerPoint和Excel的过程中的一些小技巧,用于提升工作效率. 此文会不定期的更新,更新频率完全取决于实际使用遇到的问题的次数. 目录 Word Powe ...

随机推荐

  1. gitHub使用入门和github for windows的安装教程

    在看这篇教程之前我想大家也在搜索怎样使用gitHub托管自己的项目,在使用gitHub之前我也遇到过各种问题,在网上我也搜索了很多,但总觉得网上搜索到的东西很多很杂,有的根本不知道是在表达什么.在这过 ...

  2. UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?

    选择SQLite的理由 在做UWP开发的时候我们首选的本地数据库一般都是Sqlite,我以前也不知道为啥?后来仔细研究了一下也是有原因的: 1,微软做的UWP应用大部分也是用Sqlite.或者说是微软 ...

  3. javascript动画系列第二篇——磁性吸附

    × 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性 ...

  4. MySQL优化聊两句

    原文地址:http://www.cnblogs.com/verrion/p/mysql_optimised.html MySQL优化聊两句 MySQL不多介绍,今天聊两句该如何优化以及从哪些方面入手, ...

  5. 【转】 FineBI:自助式BI工具打造业务分析的“快与准”

    如今的企业经营方式,业务对于数据分析有极大的需求,但却苦于没有数据以及工具的有效支持,业务分析仍就依赖于IT报表制作.而IT方不断地按业务需求去调研.确认业务逻辑,然后取数做报表,其中还要忍受业务的需 ...

  6. Git(1)

    安装Git 完毕 (在开始菜单打开的话,打开的不是你想要的路径,切换路径很麻烦) 1.D盘新建 GitTest 文件夹 2.打开GitTest , 在空白的地方右键, 3.单击 Git Bash He ...

  7. bootstrap

    访问Bootstrap中文网,下载bootstrap中文文档,选择用于生产环境的bootstrap. 在官网使用ctrl+f查找想要的内容. 这里记一下Visual Studio Code软件的用法: ...

  8. ASP.NET MVC Model元数据(二)

    ASP.NET MVC Model元数据(二) 前言 在上篇中,给大家留个对Model元数据的印象,并没有对Model元数据有过多的讲解,而在本篇中也不会对Model元数据的本身来解释,而是针对于它的 ...

  9. windows10简单试用(多图,连薛定谔的猫都杀死了)

    为了大家看起来方便,我的截图都是gif的,比较小,但是颜色会有色差,相信大家不介意的 昨天windows10可以下载第一时间就下了玩玩 由于是技术预览,所以不打算替换之前的系统,只装在虚拟机里玩玩就好 ...

  10. php 查询

    <h1>租房信息</h1> <?php $db = ","asdadads"); $tj = "1=1"; $tj1 = ...