1.Thread

详细介绍:https://www.cnblogs.com/cheng8/p/16147918.html

使用Thread类通过ThreadStart(无参数)或ParameterizedThreadStart(一个输入参数)类型的委托创建一个Thread对象,开启一个新线程,执行该委托传递的任务,此时线程尚未处于运行状态。调用Start()函数启动线程,当前线程继续执行。调用Join()函数可以阻塞当前线程,直到调用Join()的线程终止。

Thread类创建的线程默认为前台线程,可以通过IsBackground属性设置其为前台或后台线程。还可以通过Priority属性设置线程的优先级。

如需中止线程,调用Abort()方法,在调用该方法的线程上抛出ThreadAbortException异常,以结束该线程。线程内部可以通过try catch捕获该异常,在catch模块中进行一些必要的处理,如释放持有的锁和文件资源等,还可以通过Thread.ResetAbort()方法阻止线程的中止。但是通常来说,应当慎重使用Abort()方法,如果在当前线程中抛出该异常,其结果是可预测的,但是对于其他线程,它会中断任何正在执行的代码,有可能中断静态对象的生成,造成不可预测的结果。

Thread thread = new Thread(() =>
{
Console.WriteLine("Hello");
});
thread.Start();
Console.ReadKey();

2.线程池

ThreadPool类维护一个线程的列表,提供给用户以执行不同的小任务,减少频繁创建线程的开销。ThreadPool的使用比较简单,只需调用ThreadPool.QueueUserWorkItem()方法,传递一个WaitCallback类型的委托,线程池即从池中选择一个线程执行该任务。

但是线程池的使用也有一些限制:

  • 线程池中的线程均为后台线程,并且不能修改为前台线程
  • 不能给入池的线程设置优先级或名称
  • 对于COM对象,入池的所有线程都是多线程单元(MTA)线程,许多COM对象都需要单线程单元(STA) 线程
  • 入池的线程只适合时间较短的任务,如果线程需要长时间运行,应使用Thread类创建线程或使用Task的LongRunning选项
for (int i = 0; i < 5; ++i)
ThreadPool.QueueUserWorkItem(Do); Console.ReadKey(); static void Do(Object o)
{
for (int i = 0; i < 3; i++)
Console.WriteLine("loop:{0}, thread id: {1}", i, Thread.CurrentThread.ManagedThreadId);
}

3.Parallel类

Parallel和Task类都位于System.Threading.Task命名空间中,是对Thread和ThreadPool类更高级的抽象。Parrallel类有For()、ForEach()、Invoke()三个方法,前两者在每次迭代中调用相同的代码,实现了数据并行性,Invoke()允许同时调用不同的方法,实现任务并行性。

For()和ForEach()两者的用法类似。如下例,调用Parallel.For()方法,实现从0到10的迭代,每次迭代是并行执行的,并且从输出结果可以看出,执行顺序是不能保证的。

ParallelLoopResult result = Parallel.For(0, 10, i =>
{
Console.WriteLine("i:{0}, thread id: {1}", i, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
}); Console.WriteLine("Is completed: {0}", result.IsCompleted);
Console.ReadKey();

通过ParallelLoopState的Break()或Stop()方法,可以提前中断Parallel.For()的迭代。

ParallelLoopResult result = Parallel.For(0, 100, (i, state) =>
{
Console.WriteLine("i:{0}, thread id: {1}", i, Thread.CurrentThread.ManagedThreadId); if (i > 10)
state.Break(); Thread.Sleep(10);
}); Console.WriteLine("Is completed: {0}", result.IsCompleted);
Console.WriteLine("Lowest break iteration: {0}", result.LowestBreakIteration);
Console.ReadKey();

如需同时执行多个不同的任务,可以使用Parallel.Invoke()方法,它允许传递一个Action委托数组。

Parallel.Invoke(Func1, Func2, Func3);

4.Task类

相比于Thread类,Task类为控制线程提供了更大的灵活性。Task类可以获取线程的返回值,也可以定义连续的任务——在一个任务结束结束后开启下一个任务,还可以在层次结构中安排任务,在父任务中可以创建子任务,这样就创建了一种依赖关系,如果父任务被取消,子任务也随之取消。Task类默认使用线程池中的线程,如果该任务需长期运行,应使用TaskCreationOptions.LongRunning属性告诉任务管理器创建一个新的线程,而不是使用线程池中的线程。
如果在一个Task内部创建了另一个任务,这两者间就存在父/子的层次结构,当父任务被取消时,子任务也会被取消。如果不希望使用该层次结构,可在创建子任务时选择TaskCreationOptions.DetachedFromParent。

启动任务

TaskFactory tf = new TaskFactory();
Task t1 = tf.StartNew(TaskMethod.DoTask, "using a task factory"); Task t2 = Task.Factory.StartNew(TaskMethod.DoTask, "factory via a task"); Task t3 = new Task(TaskMethod.DoTask, "using a task constructor and start");
t3.Start(); var t4 = Task.Run(() => TaskMethod.DoTask("using Run method")); Console.ReadKey(); class TaskMethod
{
static object taskLock = new object();
public static void DoTask(object msg)
{
lock (taskLock)
{
Console.WriteLine(msg);
Console.WriteLine("Task id:{0}, Thread id :{1}",
Task.CurrentId == null ? "no task" : Task.CurrentId.ToString(),
Thread.CurrentThread.ManagedThreadId);
}
}
}

接收任务的返回值

对于任务有返回值的情况,可使用Task<TResult>泛型类,TResult定义了返回值的类型,以下代码演示了调用返回int值的任务的方法。

var t5 = new Task<int>(TaskWithResult, Tuple.Create<int, int>(1, 2));
t5.Start();
t5.Wait();
Console.WriteLine("adder results: {0}", t5.Result); Console.ReadKey(); static int TaskWithResult(object o)
{
Tuple<int, int> adder = (Tuple<int, int>)o;
return adder.Item1 + adder.Item2;
}

同步调用

调用Task类的RunSynchronously()方法,可以实现同步调用,直接在当前线程上调用该任务。

TaskMethod.DoTask("Just Main thread");
Task t1 = new Task(TaskMethod.DoTask, "using Run Sync");
t1.RunSynchronously(); class TaskMethod
{
static object taskLock = new object();
public static void DoTask(object msg)
{
lock (taskLock)
{
Console.WriteLine(msg);
Console.WriteLine("Task id:{0}, Thread id :{1}",
Task.CurrentId == null ? "no task" : Task.CurrentId.ToString(),
Thread.CurrentThread.ManagedThreadId);
}
}
}

指定连续任务

TaskFactory tf = new TaskFactory();
Task t1 = tf.StartNew(() =>
{
Console.WriteLine("Current Task id = {0}", Task.CurrentId);
Console.WriteLine("执行任务1\r\n");
Thread.Sleep(10);
}); Task t2 = t1.ContinueWith((t) =>
{
Console.WriteLine("Last Task id = {0}", t.Id);
Console.WriteLine("Current Task id = {0}", Task.CurrentId);
Console.WriteLine("执行任务2\r\n");
Thread.Sleep(10);
}); Task t3 = t2.ContinueWith(delegate (Task t)
{
Console.WriteLine("Last Task id = {0}", t.Id);
Console.WriteLine("Current Task id = {0}", Task.CurrentId);
Console.WriteLine("执行任务3\r\n");
}, TaskContinuationOptions.OnlyOnRanToCompletion); Console.ReadKey();

 

从执行结果可以看出,任务1,2,3被顺序执行,同时通过 TaskContinuationOptions 还可以指定何种情况下继续执行该任务,常用的值包括OnlyOnFaulted, OnlyOnCanceled, NotOnFaulted, NotOnCanceled等。如将上例中的OnlyOnRanToCompletion改为OnlyOnFaulted,任务2结束之后,任务3将不被执行。

对于ContinueWith()的使用,MSDN演示了更加优雅的“流式”调用方法:

 var backgroundScheduler = TaskScheduler.Default;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(delegate { DoBackgroundComputation(); },
backgroundScheduler).
ContinueWith(delegate { UpdateUI(); }, uiScheduler).
ContinueWith(delegate { DoAnotherBackgroundComputation(); },
backgroundScheduler).
ContinueWith(delegate { UpdateUIAgain(); }, uiScheduler);

5.BackgroundWorker控件

https://docs.microsoft.com/zh-cn/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.7.2

除了上述四类直接操作多线程的方法,C#还提供了BackgroundWorker控件帮助用户更简单、安全地实现多线程运算。该控件提供了DoWork, ProgressChanged 和 RunWorkerCompleted事件,为DoWork添加事件处理函数,再调用RunWorkerAsync()方法,即可创建一个新的线程执行DoWork任务。ProgressChanged和RunWorkerCompleted事件均在UI线程中执行,添加相应的处理函数,即可完成任务线程与UI线程间的交互,可用于显示任务的执行状态(完成百分比)、执行结果等。同时,该控件还提供了CancleAsync()方法,以中断线程的执行,需注意的是,调用该方法后,只是将控件的CancellationPending属性置True,用户需在程序执行过程中查询该属性以判定是否应中断线程。
 
原文:https://www.jianshu.com/p/36a65838fe46

C# 使用多线程的几种方式的更多相关文章

  1. c#使用多线程的几种方式示例详解

    本文转载自:http://www.jb51.net/article/46234.htm 本文章主要介绍了c#使用多线程的几种方式,通过示例学习c#的多线程使用方式,大家参考使用吧 (1)不需要传递参数 ...

  2. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式

    概要 本章,我们学习“常用的实现多线程的2种方式”:Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程 ...

  3. java多线程系类:基础篇:02常用的实现多线程的两种方式

    本章,我们学习"常用的实现多线程的2种方式":Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多 ...

  4. Java实现多线程的两种方式

    实现多线程的两种方式: 方式1: 继承Thread类 A: 自定义MyThread类继承Thread类 B: 在MyThread类中重写run() C: 创建MyThread类的对象 D: 启动线程对 ...

  5. c# 多线程的几种方式

    1.什么是线程? 进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源. 2.前台线程和后台线程的区别? 程序关闭时,后台线程直接关闭,但前台线程 ...

  6. 创建多线程的第一种方式——创建Thread子类和重写run方法

    创建多线程的第一种方式——创建Thread子类和重写run方法: 第二种方式——实现Runnable接口,实现类传参给父类Thread类构造方法创建线程: 第一种方式创建Thread子类和重写run方 ...

  7. Java实现多线程的三种方式

    Java多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.前两种方式启动的线程没有返回值 ...

  8. Java中实现多线程的四种方式

    Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...

  9. Java中实现多线程的两种方式之间的区别

    Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对象描述了一个单独的线程.要产生一个线 ...

  10. JAVA实现多线程的四种方式

    JAVA多线程实现方式: 1.继承Thread类(无返回值) 2.实现Runnable接口(无返回值) 3.实现Callable接口,通过FutureTask包装器来创建Threak线程(有返回值) ...

随机推荐

  1. go项目,出现too many open files

    刚开始碰到这种异常,以为是代码写的有问题,准备抽时间去改,等有时间正式此问题的时候,发现这种问题一般只会在linux系统上出现,原因如下:linux系统限制了文件打开的最大文件句柄数,系统默认一般是1 ...

  2. win7电脑休眠后只能按重启键解决办法

    一.点击"开始"后选择控制面板 二.选择"电源选项" 三.点击"更改计划设置" 四.选择"更改高级电源设置" 五.点击& ...

  3. xmind某一天突然打不开了

    某一天发现xmind双击后,应用未启动 感谢博主:https://www.cnblogs.com/helloLuoLuo/p/11926530.html 将安装目录下的两个ini文件 将文件中将App ...

  4. QT debug/moc_frmalarminfo.o:(.data.rel.ro._ZTV12FrmAlarmInfo[_ZTV12FrmAlarmInfo]+0x1c0): undefined reference to `non-virtual thunk to FrmAlarmInfo::~FrmAlarmInfo()'解决方法

    这个报错很具有迷惑性,,,我在网上还看见了ZTI12的报错,但是仔细一看发现是.o文件报错. 简单解释下.o文件(此解释来自百度): o 就是object, 也就相当于windows下编译的obj文件 ...

  5. 从个人角度谈为什么要使用git多分支?

    今天烦死了,公司的项目没有使用多分支管理,造成给某客户的打包的代码竟然需要截取gitlab节点重新打包. 在我上家公司开发Android项目的时候,使用了非常鲜明的多分支管理,当时作为项目运维,对gi ...

  6. Fiddler 抓包转成*.har文件在httprunner执行

    1.Fiddler 抓包转成*.har 参考:https://blog.csdn.net/hou_angela/article/details/91837007 2.生成httprunner项目结构 ...

  7. 利用context组件数据传递

    react的数据传递 是从父级向子级传递的.通过props.如果是很多组件需要的数据,通过props传递会非常麻烦.这个时候可以使用context. context需要可以类似于store但是也不能滥 ...

  8. selenium------关于switch_to的用法场景

    基于python3的语法,driver.switch_to_alert()的表达会出现中划线,因此需要把后面的下划线改为点.一.目前接触到的switch_to的用法包括以下几种:1. 切换到制定的wi ...

  9. 快速傅里叶变换详解(FFT)

    自己也看了几篇博客,但是对我这种不擅长推导小白来说还是有一点困难,所以自己也写一篇博客也为像我一样的小白提供思路.以下内容包含各种LaTeX渲染,如果哪里有错误欢迎大家评论留言,或者添加本人qq:14 ...

  10. 【GNU/Linux, Debian】使用cups连接HP Laserjet 1012 HB打印机

    woc我怎么第一版打的cpus Cups是个开源的打印软件,于2007年被苹果收购(包括作者负责人也被苹果雇佣),收购后任然使用GPL发行. 然而2021年年底,作为收购前负责人的他表示这个软件再也不 ...