C#多线程之任务的用法详解

目录
  • 一.启动任务
    • 1.使用线程池的任务
    • 2.同步任务
    • 3.使用单独线程的任务
  • 二.任务的结果————Future
  • 三.连续的任务
  • 四.任务的层次结构

Parallel类(https://www.jb51.net/article/244267.htm)的并行任务需要结束后才能运行后面的代码,如果想不等结束后在开始动作,可以使用Task类更好地控制并行动作。
任务表示应完成的某个工作单元。这个工作单元可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。使用任务不仅可以获得一个抽象层,还可以对底层线程进行很多控制。
任务相对Parallel类提供了非常大的灵活性。例如,可以定义连续的工作——在一个任务完成后该执行什么工作。这可以根据任务成功与否来分。还可以在层次结构中安排任务。例如,父任务可以创建新的子任务。

一.启动任务

要启动任务,可以使用TaskFactory类或Task类的构造函数和Start()方法。Task类的构造函数在创建任务上灵活性比较大。
在启动任务时,会创建Task类的一个实例,利用Action或Action<T>委托(不带参数或带一个参数),可以指定应运行的代码。

1.使用线程池的任务

线程池提供了一个后台线程的池(后面详细介绍了线程池)。线程池独自管理线程,根据需要增加或减少线程池中的线程数。线程池中的线程用于实现一些动作,之后仍然返回线程池中。
下面介绍创建线程池的任务的四种方法:
先定义一个要调用使用的方法:

  //避免写入控制台的操作交叉,这里使用lock关键字同步
        static object taskMethodLock = new object();
        static void TaskMethod(object title)
        {
            lock (taskMethodLock)
            {
                Console.WriteLine(title);
                Console.WriteLine("task id:{0},thread:{1}",Task.CurrentId,Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("is pooled thread:{0}",Thread.CurrentThread.IsThreadPoolThread);
                Console.WriteLine("is background thread:{0}",Thread.CurrentThread.IsBackground);
            }
        }

(1).使用实例化的TaskFactory类,把TaskMethod方法和TaskMethod方法的参数传递给StartNew方法:

    var tf = new TaskFactory();
    Task t1 = tf.StartNew(TaskMethod,"using a task factory");

(2).使用Task类的静态属性Factory来访问TaskFactory,以调用StartNew()方法。类似第一种,也使用了工厂,但对工厂的控制没那么全面。

    Task t2 = Task.Factory.StartNew(TaskMethod,"using factory via a task");

(3).使用Task的构造函数。实例化Task对象时任务不会执行,只是指定Created状态。接着调用Start()方法,启动任务。

    Task t3 = new Task(TaskMethod,"using a task constructor and Start");
    t3.Start();

(4).直接调用Task类的Run()方法启动任务。Run()方法没有传递带参数委托的版本,可以通过传递lambda表达式。

   Task t4 = Task.Run(()=> TaskMethod("using Run method"));
  static void Main(string[] args)
        {
            var tf = new TaskFactory();
            Task t1 = tf.StartNew(TaskMethod,"using a task factory");

            Task t2 = Task.Factory.StartNew(TaskMethod,"using factory via a task");

            Task t3 = new Task(TaskMethod,"using a task constructor and Start");
            t3.Start();

            Task t4 = Task.Run(()=> TaskMethod("using Run method"));

            Console.ReadKey();
        }

2.同步任务

任务不一定使用线程池中的线程,也可以使用其它线程。任务也可以同步运行,以相同的线程作为主调线程。
示例:

      static void RunSyncTask()
            {
                TaskMethod("main thread");
                var t = new Task(TaskMethod,"run sync");
                t.RunSynchronously();
            }

输出:

上面代码先在主线程上直接调用TaskMethod方法,然后在创建的Task上调用。从输出看到,主线程是一个前台线程,没有任务ID,也不是线程池中的线程。调用RunSynchronously方法时,会使用相同的线程,会创建一个任务。

3.使用单独线程的任务

上面将到的任务虽然不是线程池中的线程,但使用的是主线程,不是单独的,不能实现异步。
如果任务的代码应该长时间运行,就应该使用TaskCreationOptions.LongRunning告诉任务调度器创建一个新的单独线程,而不是线程池中的线程。这个线程可以不由线程池管理。当线程来自线程池时,任务调度器可以决定等待已经运行的任务完成,然后使用这个线程,而不是在线程池中创建一个新线程。对于长时间运行的线程,任务调度器会立即知道等待它们完成是不明智的做法,会创建一个新的线程。
示例:

static void LongRunTask()
            {
                var t = new Task(TaskMethod,"long running",TaskCreationOptions.LongRunning);
                t.Start();
            }

输出:

二.任务的结果————Future

任务结束时,可以把一些有用的状态信息写入共享对象中。这个共享对象必须是线程安全的。另一个选项是使用返回某个结果的任务。这种任务也叫future,因为它在将来返回一个结果。这需要使用Task类的一个泛型版本。使用这个类可以定义任务返回的结果的类型。
示例:
使用泛型类Task<TResult>,TResult是返回类型。通过构造函数,把方法传递给Func委托,第二个参数是委托的参数。

      static void Main(string[] args)
            {
                var t = new Task<Tuple<int, int>>(TaskWithResult,Tuple.Create<int,int>(8,3));
                t.Start();
                Console.WriteLine(t.Result);
                t.Wait();
                Console.WriteLine("result from task:{0},{1}", t.Result.Item1, t.Result.Item2);

                Console.ReadKey();
            }

由任务来调用来返回结果的方法可以声明为任何类型。

static Tuple<int, int> TaskWithResult(object o)
        {
            Tuple<int, int> div = (Tuple<int, int>)o;
            int result = div.Item1 / div.Item2;
            int reminder = div.Item1 % div.Item2;
            Thread.Sleep(10000);
            return Tuple.Create<int, int>(result,reminder);
        }

这里使用了元组(https://www.jb51.net/article/244045.htm).

三.连续的任务

通过任务,可以指定在任务完成后,应接着运行另一个特定任务。例如,一个使用前一个任务的结果的新任务,如果前一个任务失败了,这个任务就应执行一些清理工作。
任务处理程序(前一个任务)或者不带参数,或者带一个对象参数,而连续处理程序有一个Task类型的参数,这里可以访问前一个任务的相关信息。
示例:

//一个任务结束时,可以启动多个任务,连续任务也可以有另一个连续的任务。
        static void Main(string[] args)
        {
            Task t1 = new Task(DoFirst);
            Task t2 = t1.ContinueWith(DoSecond);
            Task t3 = t1.ContinueWith(DoSecond);
            Task t4= t2.ContinueWith(DoSecond);
            t1.Start();
            Console.ReadKey();

        }

        static void DoFirst()
        {
            Console.WriteLine("do some task:{0}",Task.CurrentId);
            Thread.Sleep(3000);
        }
        static void DoSecond(Task t)
        {
            Console.WriteLine("task {0} finished",t.Id);
            Console.WriteLine("this task id:{0}",Task.CurrentId);

        }

无论前一个任务是如何结束,前面的连续任务总是在前一个任务结束时启动。使用TaskContinuationOptions枚举中的值,可以指定,连续任务只有在任务成功或失败时启动。

    Task t5 = t1.ContinueWith(DoSecond,TaskContinuationOptions.OnlyOnFaulted);

四.任务的层次结构

利用任务连续性,可以在一个任务结束后启动另一个任务。任务也可以构成一个层次结构。在一个任务中启动一个新的任务时,就启动了一个父/子层次结构。取消父任务,也会取消子任务。
创建子任务与创建父任务的代码相同,唯一区别就就是子任务从另一个任务内部创建。
示例:

static void Main(string[] args)
        {
            Task t = new Task(ParentTask);
            t.Start();
            Console.ReadKey();

        }

        static void ParentTask()
        {
            Console.WriteLine("parent task id:{0}",Task.CurrentId);
            var child = new Task(ChildTask);
            child.Start();
            Console.WriteLine("parent  create child");
        }

        static void ChildTask()
        {
            Console.WriteLine("child task");

        }

如果父任务在子任务之前结束,父任务的状态就是WaitingForChildrenToComplete。所有子任务也结束时,父任务的状态就是RanToCompletion.

到此这篇关于C#多线程之任务的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#多线程传递参数及任务用法示例

    本文实例讲述了C#多线程传递参数及任务用法.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleSample { class Program { static void Main(string[] args) { Console.WriteLine("这是主线程&

  • C#(asp.net)多线程用法示例(可用于同时处理多个任务)

    本文实例讲述了C#(asp.net)多线程用法.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Threading; using System.Web.UI.WebControls; public partial class muti_thread : System.Web.

  • C#多线程开发之任务并行库详解

    目录 前言 任务并行库 一.创建任务 二.使用任务执行基本操作 三.处理任务中的异常 总结 前言 之前学习了线程池,知道了它有很多好处. 使用线程池可以使我们在减少并行度花销时节省操作系统资源.可认为线程池是一个抽象层,其向程序员隐藏了使用线程的细节,使我们可以专心处理程序逻辑,而不是各种线程问题. 但也不是说我们所有的项目中都上线程池,其实它也有很多弊端,比如我们需要自定义使用异步委托的方式才可以将线程中的消息或异常传递出来.这些如果在一个大的软件系统中,会导致软件结构过于混乱,各个线程之间消

  • C#如何对多线程、多任务管理(demo)

    下面一段内容有项目需求有项目分析,通过一个小demo给大家展示下C#如何对多线程.多任务管理的. 项目需求:假设多个任务需要执行,每个任务不是一时半会能完成(需要能看到中间执行状况): 多个任务 根据条件不同 可能需要不同的处理 项目分析: 多线程并发执行多任务: 对任务进行管理,追踪中间执行状态: 运用策略模式抽象执行类: public enum TaskStatus { wait = 0, working = 1, stop = 2, suspend = 3, complete = 4, f

  • C#多线程系列之任务基础(二)

    目录 判断任务状态 再说父子任务 组合任务/延续任务 复杂的延续任务 并行(异步)处理任务 并行(同步)处理任务 并行任务的 Task.WhenAny 并行任务状态 循环中值变化问题 定时任务 TaskScheduler 类 判断任务状态 属性 说明 IsCanceled 获取此 Task 实例是否由于被取消的原因而已完成执行. IsCompleted 获取一个值,它表示是否已完成任务. IsCompletedSuccessfully 了解任务是否运行到完成. IsFaulted 获取 Task

  • C#多线程系列之任务基础(一)

    目录 多线程编程 多线程编程模式 探究优点 任务操作 两种创建任务的方式 Task.Run() 创建任务 取消任务 父子任务 任务返回结果以及异步获取返回结果 捕获任务异常 全局捕获任务异常 多线程编程 多线程编程模式 .NET 中,有三种异步编程模式,分别是基于任务的异步模式(TAP).基于事件的异步模式(EAP).异步编程模式(APM). 基于任务的异步模式 (TAP) :.NET 推荐使用的异步编程方法,该模式使用单一方法表示异步操作的开始和完成.包括我们常用的 async .await

  • C#多线程系列之任务基础(三)

    目录 TaskAwaiter 延续的另一种方法 另一种创建任务的方法 实现一个支持同步和异步任务的类型 Task.FromCanceled() 如何在内部取消任务 Yield 关键字 补充知识点 TaskAwaiter 先说一下 TaskAwaiter,TaskAwaiter 表示等待异步任务完成的对象并为结果提供参数. Task 有个 GetAwaiter() 方法,会返回TaskAwaiter 或TaskAwaiter<TResult>,TaskAwaiter 类型在 System.Run

  • C#多线程之任务的用法详解

    目录 一.启动任务 1.使用线程池的任务 2.同步任务 3.使用单独线程的任务 二.任务的结果————Future 三.连续的任务 四.任务的层次结构 Parallel类(https://www.jb51.net/article/244267.htm)的并行任务需要结束后才能运行后面的代码,如果想不等结束后在开始动作,可以使用Task类更好地控制并行动作.任务表示应完成的某个工作单元.这个工作单元可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程.使用任务不仅可以获得一个抽

  • 对python 多线程中的守护线程与join的用法详解

    多线程:在同一个时间做多件事 守护线程:如果在程序中将子线程设置为守护线程,则该子线程会在主线程结束时自动退出,设置方式为thread.setDaemon(True),要在thread.start()之前设置,默认是false的,也就是主线程结束时,子线程依然在执行. thread.join():在子线程完成运行之前,该子线程的父线程(一般就是主线程)将一直存在,也就是被阻塞 实例: #!/usr/bin/python # encoding: utf-8 import threading fro

  • C#多线程用法详解

    目录 一.基本概念 1.进程 2.线程 二.多线程 2.1 System.Threading.Thread类 2.2线程的常用属性 2.2.1 线程的标识符 2.2.2 线程的优先级别 2.2.3 线程的状态 2.2.4 System.Threading.Thread的方法 2.3 前台线程和后台线程 2.4 线程同步 2.5 跨线程访问 2.6 终止线程 三.同步和异步 四.回调 一.基本概念 1.进程 首先打开任务管理器,查看当前运行的进程: 从任务管理器里面可以看到当前所有正在运行的进程.

  • C#多线程系列之async和await用法详解

    目录 async和await async await 从以往知识推导 创建异步任务 创建异步任务并返回Task 异步改同步 说说awaitTask 说说 asyncTask<TResult> 同步异步? Task封装异步任务 关于跳到await变异步 为什么出现一层层的await async和await async 微软文档:使用 async 修饰符可将方法.lambda 表达式或匿名方法指定为异步. 使用 async 修饰的方法,称为异步方法. 例如: 为了命名规范,使用 async 修饰的

  • Java RandomAccessFile的用法详解

    RandomAccessFile RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了.这些记录的大小不必相同:但是其大小和位置必须是可知的.但是该类仅限于操作文件. RandomAccessFile不属于InputStream和OutputStream类系的.实际上,除了实现DataInput和 DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫

  • Bootstrap Fileinput文件上传组件用法详解

    最近时间空余,总结了一些关于bootstrap fileinput组件的一些常见用法,特此分享到我们平台,供大家参考,同时也方便以后的查找.本文写的不好还请见谅. 一.效果展示 1.原始的input type='file',简直不忍直视. 2.不做任何装饰的bootstrap fileinput:(bootstrap fileinput初级进化) 3.bootstrap fileinput高级进化:中文化.可拖拽上传.文件扩展名校验(如果不是需要的文件,不让上传) 拖拽上传 上传中 4.boot

  • Java多线程中ReentrantLock与Condition详解

    一.ReentrantLock类 1.1什么是reentrantlock java.util.concurrent.lock中的Lock框架是锁定的一个抽象,它允许把锁定的实现作为Java类,而不是作为语言的特性来实现.这就为Lock的多种实现留下了空间,各种实现可能有不同的调度算法.性能特性或者锁定语义.ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,但是添加了类似锁投票.定时锁等候和可中断锁等候的一些特性.此外,它还提供了在激烈争用情况下更

  • java.text.DecimalFormat用法详解

    简要 DecimalFormat 的 pattern 都包含着 正负子 pattern ,例如 "#,##0.00;(#,##0.00)": /** * Created by Shuai on 2016/7/11. */ public class Main { public static void main(String[] args) { // 正值 BigDecimal bigDecimal = BigDecimal.valueOf(-12211151515151.541666);

  • Ubuntu常用指令及用法详解

    1.ls 这个相当于Windows 下的dir命令,可以列出当前窗口或指定窗口下的内容. 2.rm 这个相当于Windows 下的del 和rmdir 命令,可以删除文件及文件夹. 常见用法:rm -rf/home/ubuntu/.cache (删除 /home/ubuntu/.cache这个文件夹) 禁忌:rm-rf /* 这个命令会删除根分区下所有文件,在某些efi机器上还会删除主板固件,造成主板固件丢失从而无法开机(比操作系统无法启动还严重). 3.chmod 更改文件权限,类似于Wind

  • C#中backgroundWorker类的用法详解

    1.在 WinForms 中,有时要执行耗时的操作,在该操作未完成之前操作用户界面,会导致用户界面停止响应.解决的方法就是新开一个线程,把耗时的操作放到线程中执行,这样就可以在用户界面上进行其它操作.新建线程可以用 Thread 类,可以实现多线程同时操作.简单的方法可以通过 BackgroundWorker 类实现. BackgroundWorker 可以用来更新UI界面,但是通常用来Progressbar(进度条)控件 例如更新UI private void Form1_Load(objec

随机推荐