[多线程]thread,threadpool,task及TPL知识点整理

简单理解

Thread:是一个指令序列,个体对象。

Threadpool:在使用Thread的过程中,程序员要为每个希望并发的序列new一个线程,很麻烦,因此希望有一个统一管理线程的方法,程序员就不需要关注线程的申请管理问题,所以就对Thread进行一系列封装,有了ThreadPool。使用Threadpool,把需要并发的序列添加进线程池,线程池根据其线程列表中的线程的空闲情况,动态为并发序列申请线程。

Task:再后来,程序员发现在使用Threadpool的过程当中还是存在很多不便,比如:(1)ThreadPool不支持线程的取消、完成、失败通知等交互性操作;(2)ThreadPool不支持线程执行的先后次序;那么怎么办,那就继续对ThreadPool进行封装呗,于是.net Framework4.0有了TPL和Task。

Thread

  选用Thread的一个重要原因之一是因为它的Thread.Sleep()成员,因为它在Task或ThreadPool中都没有等价物。不过如果不会带来太多没必要的复杂性,可以考虑用一个计时器代替Sleep()。

  对于Thread这个知识点,重点在于同步、死锁和竞态等问题,感觉关键点在于对lock()和WaitHandler的理解。这里,介绍下参数的传递和获取。

  下面这部分主要查看MSDN的文档:http://msdn.microsoft.com/en-us/library/wkays279.aspx

  • 向线程传递参数

  因为Thread的构造函数需要传递一个无返回类型和无参的委托引用,因此最理想的办法是把需要传递给线程的方法封装在一个类里面,然后将需要传递给这个线程的参数定义为该类的字段。比如,要把下面这个带参函数通过线程调用:

1 public double CalArea(double length,double width)
2 {
3 double Area=length*width;
4 return Area;
5 }

  

  可使用如下方法进行:

 1 public class AreaClass
 2 {
 3 public double width;
 4 public double length;
 5 public double area;
 6 public void CalArea()
 7 {
 8 area=width*length;
 9 }
10 }
11
12 public void TestAreaCal()
13 {
14 AreaClass areaCal=new AreaClass();
15 System.Threading.Thread thread1=new System.Threading.Thread(areaCal.CalArea);
16 areaCal.width=2;
17 areaCal.length=3;
18 thread1.start();
19 }

  但要注意,如果在执行完thread1.start()之后,立刻查看areaCal.area的值,并不一定就存在了。要获得有效的返回值得简单方法将在下面介绍。

 

  • 获取线程返回值

  简单方法是使用BackgroundWorker类。利用BackgroundWorker类管理你的线程,并在线程执行完毕后产生一个事件,然后在对应的事件处理函数中处理结果。

 1 class AreaClass2
 2 {
 3     public double Base;
 4     public double Height;
 5     public double CalcArea()
 6     {
 7         // Calculate the area of a triangle.
 8         return 0.5 * Base * Height;
 9     }
10 }
11
12 private System.ComponentModel.BackgroundWorker BackgroundWorker1
13     = new System.ComponentModel.BackgroundWorker();
14
15 private void TestArea2()
16 {
17     InitializeBackgroundWorker();
18
19     AreaClass2 AreaObject2 = new AreaClass2();
20     AreaObject2.Base = 30;
21     AreaObject2.Height = 40;
22
23     // Start the asynchronous operation.
24     BackgroundWorker1.RunWorkerAsync(AreaObject2);
25 }
26
27 private void InitializeBackgroundWorker()
28 {
29     // Attach event handlers to the BackgroundWorker object.
30     BackgroundWorker1.DoWork +=
31         new System.ComponentModel.DoWorkEventHandler(BackgroundWorker1_DoWork);
32     BackgroundWorker1.RunWorkerCompleted +=
33         new System.ComponentModel.RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);
34 }
35
36 private void BackgroundWorker1_DoWork(
37     object sender,
38     System.ComponentModel.DoWorkEventArgs e)
39 {
40     AreaClass2 AreaObject2 = (AreaClass2)e.Argument;
41     // Return the value through the Result property.
42     e.Result = AreaObject2.CalcArea();
43 }
44
45 private void BackgroundWorker1_RunWorkerCompleted(
46     object sender,
47     System.ComponentModel.RunWorkerCompletedEventArgs e)
48 {
49     // Access the result through the Result property.
50     double Area = (double)e.Result;
51     MessageBox.Show("The area is: " + Area.ToString());
52 }

ThreadPool

ThreadPool是一个静态类。

管理过程:

许多程序生成的线程有一大半时间处在休眠状态,直到产生一个事件唤醒它们,另一部分线程只会被周期性地唤醒以更改状态信息。ThreadPool可以通过为你的应用程序提供由系统管理的线程来提高应用程序对线程的使用率。线程池中会有一个线程监看线程池队列中的等待操作,当线程池中的线程完成它的任务后,它就会回到等待线程的队列当中,等待重新使用。如果线程池中的所有线程都在运行,那么新的任务会加进等待队列中。对线程的重复使用降低了为每一个任务都开启一个新线程的造成的程序开销。

特点:

ThreadPool里面的线程管理是由类库(或操作系统)完成的,而不是由应用程序进行管理。

线程池里面的线程都是后台线程(即它们的isBackgroud属性为true)。也就意味着当所有前台线程都运行完毕后,线程池的线程不会保持程序的运行。

线程池一般使用在服务应用程序上。每个进来的请求都分配给线程池中的每一个线程,这样就可以异步处理各个请求。

使用:

线程池的简单使用可通过QueueUserWorkItem方法进行。如果需要向任务中传递参数,可以通过传递一个state object来进行,如果有多个参数要传递,可将多个参数封装在结构或对象的字段当中。但要注意,一旦一个方法加入了线程队列,就无法取消该方法的执行

public static void Main()
{
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ASynTask),ASynData);
//other code snippets......
}

public void ASynTask(object state)
{
//codes need to executed asynchronously
}

线程池的大小可以通过GetMaxThreads或SetMaxThreads方法访问或设置,但注意过大或过小都会影响程序的性能。

另外,获取ThreadPool返回值有两种方法,具体查看上面Thread所讲的办法。

Task和TPL

  关于Task和TPL的知识还是以后再详细介绍。这里简单讲下Task的主要特征。

  •   Task包含一个ContinueWith()方法,它的作用是将任务链接起来,只要链中的第一个任务完成,就会触发已登记在它之后执行的其它任务。还有个有趣的地方,可以用ContinueWith()添加多个任务,在先驱任务(父任务)完成后,添加的所有任务都会开始并行运行。另外,可以在ContinueWith()语句有一个TaskContinuationOpitons枚举值,通过指定该值,可根据先驱任务的运行情况决定是否运行子任务。
  •   支持协作式取消任务。通过使用System.Threading.CancellationToken来进行。一个线程通过设置CancellationToken(struct类型)请求取消任务,运行的线程通过检查该CancellationToken决定是否继续运行。还有个值得关注的地方就是,通过CancellationToken终止的线程,如果不会在某些方面造成破坏,不会使线程的Faulted属性为true。
 1 public static void Main()
 2 {
 3     CancellationTokenSource cancellationTokenSource=new CancellationTokenSource();
 4     Task task.Task.Factory.StartNew(()=>LoopWork(cancellationTokenSource.Token),cancellationTokenSource.Token);
 5     Thread.sleep(1000);
 6     cancellationTokenSource.Cancel();
 7    task.wait();
 8 }
 9
10 private static void LoopWork(CancellationToken cancellationToken)
11 {
12    while(!cancellationToken.IsCancellationRequested)
13   {
14      //work need to be done
15   }
16 }

Task还支持IDisposable方法,这是支持Wait()功能所需要的。

并行迭代

在使用for循环迭代时,处理器会按顺序依次迭代。但如果每次迭代之间互不影响,而且如果系统有多个处理器,我们可以让每个处理器都负责一个迭代,从而提高效率。

相关类:System.Threading.Tasks.Parallel。

相关方法:Parallel.For()和Parallel.Foreach()

注意三点:计算机具有多个处理器,并行迭代的顺序不是固定的,在使用时还要同时考虑迭代内部代码的非原子性带来的竞态问题。

 1 //将10000个小写单词变成大写
 2 static void Main()
 3 {
 4    int iterations=10000;
 5    string[] word=new string[iterations];
 6    //此处初始化word,输入10000个小写单词
 7   Parallel.For(0,iterations,(i)=>
 8     {
 9        word[i]=word[i].toUpper();
10     });
11 }    
时间: 2024-10-02 22:41:17

[多线程]thread,threadpool,task及TPL知识点整理的相关文章

.net 多线程 Thread ThreadPool Task

先准备一个耗时方法 /// <summary>/// 耗时方法/// </summary>/// <param name="name"></param>private void DoSomeThing(string name){                 Console.WriteLine($"开始执行{name}, {Thread.CurrentThread.ManagedThreadId.ToString("

异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 threadpool:线程池使用,设置线程池,ManualResetEvent9 Task初步接触 10 task:waitall waitany continueWhenAny continueWhenAll  11并行运算Parallel 12 异常处理.线程取消.多线程的临时变量和lock13 A

Java 多线程知识点整理

1.如何停止一个正在运行的线程? 使用共享变量,用volatile变量修饰,线程循环去访问该变量.如果线程因为发生某些事件进行阻塞,可以使用Thread的interrupt方法.可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码. class MyThread extends Thread { volatile boolean stop = false; public void run() { while (!stop) { System.out.println(g

C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!

说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行:对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行,这样主线程就不会阻塞,可以继续往下执行:等到后台线程执行完毕,再通知主线程,然后做出对应操作! 在C#中开启新线程比较简单 static void Main(string[]

【知识点整理】Oracle中NOLOGGING、APPEND、ARCHIVE和PARALLEL下,REDO、UNDO和执行速度的比较

[知识点整理]Oracle中NOLOGGING.APPEND.ARCHIVE和PARALLEL下,REDO.UNDO和执行速度的比较 1  BLOG文档结构图 2  前言部分 2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 系统和会话级别的REDO和UNDO量的查询 ② NOLOGGING.APPEND.ARCHIVE和PARALLEL下,REDO.UNDO和执行速度的比较(重点)   Tips: ① 本文

2.匿名类,匿名类对象,private/protected/public关键字、abstract抽象类,抽象方法、final关键字的使用,多线程Thread类start方法原理

package com.bawei.multithread; //注意:模板方法我们通常使用抽象类或者抽象方法!这里我们为了方便在本类中使用就没有使用抽象类/抽象方法 public class TemplateThread { //如果这个方法不想被子类或者别人随意改动[这样子类就不能覆写该方法了],这里方法就要设置为final方法 public final void println(String message){ System.out.println("###################

C#初级知识点整理及VS的简单使用

C#预处理器指令#define #undef 声明一个不需赋值的变量注意的一点事它必须放到using 上面,如 #define TEST using System.xxx; public class Task { public Task() { } } #region #endregion这是一组用来帮助管理代码布局的指令 #if #else #elif #endifif else 没什么好说的.不过这个一般要跟#define搭配使用 #warn #error输出警告或错误 #if DEBUG

C#多线程和异步——Task和async/await详解

阅读目录 一.什么是异步 二.Task介绍 1 Task创建和运行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延续操作(WhenAny/WhenAll/ContinueWith) 4 Task的任务取消(CancellationTokenSource) 三.异步方法(async/await) 回到顶部 一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时

Java基础第八天听课总结(2)--多线程&Thread

进程 在任务管理器中查看进程,应用程序对应一个进程 进程之间的内存是隔离的,进程间是通过套接字通信Socket通信 什么是线程? ------------------------------ 线程是程序执行过程中,并发执行的代码段 线程之是可以共享内存. 线程的执行是从上往下按序执行的. 创建线程方式一 继承Thread 子类覆盖中的run方法,将线程执行的代码存放在run中 建立子类对象的同时线程也被创建. 通过调用start方法开启线程 Thread是线程类 start() //通知CPU可