C#中级-常用多线程操作(持续更新)

一、前言

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

二、关于线程

(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 = 0; index < PackCount; index++)
{
   int Threadindex = index;
   var task = new Task(() =>
         {
                // do something   }
   });
   tasks[Threadindex] = task;
   task.Start();
}
Task.WaitAll(tasks); //等待所有线程完成
//Task.WaitAny(tasks); //等待一个线程完成继续执行主程序

//Task.FactoryTask.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上有一招:

 1 private void RaiseEventOnUIThread(Delegate theEvent, object[] args)
 2 {
 3       foreach (Delegate d in theEvent.GetInvocationList())
 4       {
 5             ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
 6             if (syncer == null)  //静态函数为null 7             {
 8                     d.DynamicInvoke(args);
 9             }
10             else
11             {
12                     syncer.BeginInvoke(d, args); //在创建了此对象的线程上异步执行委托
13             }
14        }
15 }

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

时间: 2024-08-02 14:48:39

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

linux常用命令(持续更新)

1. 查看linux系统版本: 登录到服务器执行 lsb_release -a ,即可列出所有版本信息, 例如: [[email protected]_SYS ~]# lsb_release -a 查看内核版本: [[email protected] ~]# uname -aLinux localhost.localdomain 2.6.18-274.el5 #1 SMP Fri Jul 8 17:36:59 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux 查看l

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

项目正在如火如荼的开展,代码量的繁多不得不令我们运用 git 这个有用的工具去管理我们共同协作的代码 git 在这里不作什么介绍了,百度一大堆的教程 首推廖雪峰老师的:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 还有一篇我觉得写得不错的教程:http://blog.jobbole.com/78960/ 好了,用了一天的git 记录一下自己觉得比较常用的命令语句: ------

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

本文主要记录在学习以及使用Jenkins过程中常用的对我们有帮助的插件,同时本文将会持续进行更新.如果大家发现有其他野很好用的插件,也欢迎参照下面的格式,在评论中进行回复反馈. 一.通用插件 1.Embeddable Build Status Icon 链接:https://ci.jenkins-ci.org/job/lib-jira-api/badge/ | https://wiki.jenkins-ci.org/display/JENKINS/Embeddable+Build+Status+

android开发常用组件【持续更新中。。。】

UI相关 图片 Android-Universal-Image-Loader:com.nostra13.universalimageloader:异步加载.缓存.显示图片 ImageLoader:com.novoda.imageloader:异步加载.缓存.显示图片 picasso:com.squareup.picasso:功能强大的图片下载缓存库 PhotoView:uk\co\senab\photoview:支持缩放和各种手势的ImageView ListView JazzyListView

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

本地仓库操作git int                                 初始化本地仓库git add .                             将所有文件添加到本地仓库git commit -m "提交文件"      把文件提交到仓库,双引号内是提交注释 和远程仓库建立联系git remote add origin [email protected]:wangjiax9/beautifulDay.git      关联github远程仓库git

常用集合类数据结构(持续更新中)

零.约定 1.  如果没有特殊说明,均指jdk 6,7,8一样 2.  源码分析出来的,有错误,请务必告知,谢谢 一.链表 1.  ArrayList<>() a) 基本介绍  i. 动态数组的数据结构 ii.新增时,容量超过当前容量,会新new一个数组 iii. 随机查询忧,添加删除劣 b)         常用构造函数 i.              ArrayList() jdk6初始化为容量10的数组 jdk7,8初始化为容量为0的数组(EMPTY_ELEMENTDATA) ii.  

git常用命令(持续更新)

1.设置用户名和邮箱: git config --global user.name ""; git config --global user.email ""; git config --list 查看配置 2.提交: git add *.java git commit -m ""; git push origin dev 3.更新: git pull 4.强制更新: git reset --hard origin/dev 5.强制回退到某个历史

Ubuntu下常用软件汇总(持续更新)

最近开始用Ubuntu了,好多软件都不是常用的了,在这边留底,以免忘记.如果没有写安装方法,则直接在软件源中可以找到 UNetbootin U盘制作工具,制作Ubuntu的安装U盘超好用 Brasero 光盘刻录工具 Unity Tweak Tool 主题设置工具 好用,比如什么改系统字体之类的,全部搞定 lwqq QQ在插件版,最受不了的就是不能发送文件和图片,不过能收,单单比稳定易用,比wineqq和webqq都要强,wineqq太不稳定了,webqq又没有实时消息提醒!!可以作为主聊天,必

git的常用语句(持续更新中)

mkdir learngit // 创建文件夹cd learngit //跳转到指定文件夹pwd //显示当前文件夹路径git init //把这个目录变成可管理的仓库git add learngit.txt // 添加文件到提交目录git commit -m"文件相关信息" // 提交文件并附属文件所更改的描述信息git status //常看当前状态,用于描述文件所更新过的相关状态git diff //用于查看所更改过的相关信息git log //用于查看更改过的日志文件git r