首页  ·  知识 ·  云计算
NET多线程编程
网友  腾讯科技   综合  编辑:DEZAI   图片来源:网络
线程的基本概念 线程是程序执行的基本原子单位. 一个进程可以由多个线程组成. 每个线程
线程的基本概念
 线程是程序执行的基本原子单位. 一个进程可以由多个线程组成.
 每个线程都维护异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的CPU 寄存器组和堆栈。
 在分布式编程中,正确使用线程能够很好的提高应用程序的性能及运行效率.实现原理是将一个进程分成多个线程,然后让它们并发异步执行,来提高运行效率.
 并发执行并不是同时执行(占有CPU),任意时刻还是只能有一个线程占用CPU,只不过是它们争夺CPU频繁一些,感觉到他们似乎都在运行.
什么时候用线程?
 一般情况下,如果多个线程在执行时都要抢占某一个资源或某几个资源,则最好不用异步线程执行.因为它们是并发执行,很可能同时争夺某个资源有CPU,这时要么执行资源分配算法(比如要判断哪个线程优先级高,这要花费时间),或者是按时间片算法(这样要付出轮询CUP/交接/让出CPU所需的时间).
 如果多个线程所需要的系统资源是比较均匀的,这时完全可以让它们异步并发执行,
使用线程的缺点
 统将为进程和线程所需的上下文信息使用内存。因此,可以创建的进程、AppDomain 对象和线程的数目会受到可用内存的限制。
 跟踪大量的线程将占用大量的处理器时间。如果线程过多,则其中大多数线程都不会产生明显的进度。如果大多数当前线程处于一个进程中,则其他进程中的线程的调度频率就会很低。
 使用许多线程控制代码执行非常复杂,并可能产生许多错误。
 销毁线程需要了解可能发生的问题并对那些问题进行处理。
线程池
 许多应用程序创建的线程都要在休眠状态中消耗大量时间,以等待事件发生。这样会浪费资源。
 线程池通过为应用程序提供一个由系统管理的辅助线程池使您可以更为有效地使用线程。一个线程监视排到线程池的若干个等待操作的状态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。
 实际上,如果要执行一些需要多个线程的较短任务,则使用ThreadPool 类是利用多个线程的最方便且最好的方法。使用线程池使系统能够不仅针对此进程而且针对计算机上的其他进程(您的应用程序对其一无所知)对此情况进行优化以达到更好的吞吐量。使用线程池使系统能够在考虑到计算机上的所有当前进程后对线程时间片进行优化。
ThreadPool
 线程池在首次创建ThreadPool 类的实例时被创建。线程池具有每个可用处理器25 个线程的默认限制
 可以将与等待操作不相关的工作项排列到线程池。若要请求由线程池中的一个线程来处理工作项,请调用QueueUserWorkItem 方法。此方法将对将被从线程池中选定的线程调用的方法或委托的引用用作参数。一个工作项排入队列后就无法再取消它。
 
创建和终止线程
view source print01 using System; 
02 using System.Threading; 
03   
04 public class Worker 
05 { 
06     // 启动线程时调用此方法。 
07     public void DoWork() 
08     { 
09         while (!_shouldStop) 
10         { 
11             Console.WriteLine("worker thread: working..."); 
12         } 
13         Console.WriteLine("worker thread: terminating gracefully."); 
14     } 
15     public void RequestStop() 
16     { 
17         _shouldStop = true; 
18     } 
19     // Volatile 用于向编译器提示此数据 
20     // 成员将由多个线程访问。 
21     private volatile bool _shouldStop; 
22 } 
23   
24 public class WorkerThreadExample 
25 { 
26     static void Main() 
27     { 
28         // 创建线程对象。这不会启动该线程。 
29         Worker workerObject = new Worker(); 
30         Thread workerThread = new Thread(workerObject.DoWork); 
31   
32         // 启动辅助线程。 
33         workerThread.Start(); 
34         Console.WriteLine("main thread: Starting worker thread..."); 
35   
36         // 循环直至辅助线程激活。 
37         while (!workerThread.IsAlive); 
38   
39         // 为主线程设置 1 毫秒的休眠, 
40         // 以使辅助线程完成某项工作。 
41         Thread.Sleep(1); 
42   
43         // 请求辅助线程自行停止: 
44         workerObject.RequestStop(); 
45   
46         // 使用 Join 方法阻塞当前线程,  
47         // 直至对象的线程终止。 
48         workerThread.Join(); 
49         Console.WriteLine("main thread: Worker thread has terminated."); 
50     } 
51 }
 
使用线程池
 
 
view source print01 using System; 
02 using System.Threading; 
03   
04 // Fibonacci 类为使用辅助 
05 // 线程执行长时间的 Fibonacci(N) 计算提供了一个接口。 
06 // N 是为 Fibonacci 构造函数提供的,此外还提供了 
07 // 操作完成时对象发出的事件信号。 
08 // 然后,可以使用 FibOfN 属性来检索结果。 
09 public class Fibonacci 
10 { 
11     public Fibonacci(int n, ManualResetEvent doneEvent) 
12     { 
13         _n = n; 
14         _doneEvent = doneEvent; 
15     } 
16   
17     // 供线程池使用的包装方法。 
18     public void ThreadPoolCallback(Object threadContext) 
19     { 
20         int threadIndex = (int)threadContext; 
21         Console.WriteLine("thread {0} started...", threadIndex); 
22         _fibOfN = Calculate(_n); 
23         Console.WriteLine("thread {0} result calculated...", threadIndex); 
24         _doneEvent.Set(); 
25     } 
26   
27     // 计算第 N 个斐波纳契数的递归方法。 
28     public int Calculate(int n) 
29     { 
30         if (n <= 1) 
31         { 
32             return n; 
33         } 
34         else
35         { 
36             return Calculate(n - 1) + Calculate(n - 2); 
37         } 
38     } 
39   
40     public int N { get { return _n; } } 
41     private int _n; 
42   
43     public int FibOfN { get { return _fibOfN; } } 
44     private int _fibOfN; 
45   
46     ManualResetEvent _doneEvent; 
47 } 
48   
49 public class ThreadPoolExample 
50 { 
51     static void Main() 
52     { 
53         const int FibonacciCalculations = 10; 
54   
55         // 每个 Fibonacci 对象使用一个事件 
56         ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations]; 
57         Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations]; 
58         Random r = new Random(); 
59   
60         // 使用 ThreadPool 配置和启动线程: 
61         Console.WriteLine("launching {0} tasks...", FibonacciCalculations); 
62         for (int i = 0; i < FibonacciCalculations; i++) 
63         { 
64             doneEvents[i] = new ManualResetEvent(false); 
65             Fibonacci f = new Fibonacci(r.Next(20, 40), doneEvents[i]); 
66             fibArray[i] = f; 
67             ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i); 
68         } 
69   
70         // 等待池中的所有线程执行计算... 
71         WaitHandle.WaitAll(doneEvents); 
72         Console.WriteLine("Calculations complete."); 
73   
74         // 显示结果... 
75         for (int i = 0; i < FibonacciCalculations; i++) 
76         { 
77             Fibonacci f = fibArray[i]; 
78             Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN); 
79         } 
80     } 
81 }
 
线程同步和互交
 
view source print001 using System; 
002 using System.Threading; 
003 using System.Collections; 
004 using System.Collections.Generic; 
005   
006 // 将线程同步事件封装在此类中,  
007 // 以便于将这些事件传递给 Consumer 和 
008 // Producer 类。 
009 public class SyncEvents 
010 { 
011     public SyncEvents() 
012     { 
013         // AutoResetEvent 用于“新项”事件,因为 
014         // 我们希望每当使用者线程响应此事件时, 
015         // 此事件就会自动重置。 
016         _newItemEvent = new AutoResetEvent(false); 
017   
018         // ManualResetEvent 用于“退出”事件,因为 
019         // 我们希望发出此事件的信号时有多个线程响应。 
020         // 如果使用 AutoResetEvent,事件 
021         // 对象将在单个线程作出响应之后恢复为  
022         // 未发信号的状态,而其他线程将 
023         // 无法终止。 
024         _exitThreadEvent = new ManualResetEvent(false); 
025   
026         // 这两个事件也放在一个 WaitHandle 数组中,以便 
027         // 使用者线程可以使用 WaitAny 方法 
028         // 阻塞这两个事件。 
029         _eventArray = new WaitHandle[2]; 
030         _eventArray[0] = _newItemEvent; 
031         _eventArray[1] = _exitThreadEvent; 
032     } 
033   
034     // 公共属性允许对事件进行安全访问。 
035     public EventWaitHandle ExitThreadEvent 
036     { 
037         get { return _exitThreadEvent; } 
038     } 
039     public EventWaitHandle NewItemEvent 
040     { 
041         get { return _newItemEvent; } 
042     } 
043     public WaitHandle[] EventArray 
044     { 
045         get { return _eventArray; } 
046     } 
047   
048     private EventWaitHandle _newItemEvent; 
049     private EventWaitHandle _exitThreadEvent; 
050     private WaitHandle[] _eventArray; 
051 } 
052   
053 // Producer 类(使用一个辅助线程) 
054 // 将项异步添加到队列中,共添加 20 个项。 
055 public class Producer  
056 { 
057     public Producer(Queue<int> q, SyncEvents e) 
058     { 
059         _queue = q; 
060         _syncEvents = e; 
061     } 
062     public void ThreadRun() 
063     { 
064         int count = 0; 
065         Random r = new Random(); 
066         while (!_syncEvents.ExitThreadEvent.WaitOne(0, false)) 
067         { 
068             lock (((ICollection)_queue).SyncRoot) 
069             { 
070                 while (_queue.Count < 20) 
071                 { 
072                     _queue.Enqueue(r.Next(0, 100)); 
073                     _syncEvents.NewItemEvent.Set(); 
074                     count++; 
075                 } 
076             } 
077         } 
078         Console.WriteLine("Producer thread: produced {0} items", count); 
079     } 
080     private Queue<int> _queue; 
081     private SyncEvents _syncEvents; 
082 } 
083   
084 // Consumer 类通过自己的辅助线程使用队列 
085 // 中的项。Producer 类使用 NewItemEvent  
086 // 将新项通知 Consumer 类。 
087 public class Consumer 
088 { 
089     public Consumer(Queue<int> q, SyncEvents e) 
090     { 
091         _queue = q; 
092         _syncEvents = e; 
093     } 
094     public void ThreadRun() 
095     { 
096         int count = 0; 
097         while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1) 
098         { 
099             lock (((ICollection)_queue).SyncRoot) 
100             { 
101                 int item = _queue.Dequeue(); 
102             } 
103             count++; 
104         } 
105         Console.WriteLine("Consumer Thread: consumed {0} items", count); 
106     } 
107     private Queue<int> _queue; 
108     private SyncEvents _syncEvents; 
109 } 
110   
111 public class ThreadSyncSample 
112 { 
113     private static void ShowQueueContents(Queue<int> q) 
114     { 
115         // 对集合进行枚举本来就不是线程安全的, 
116         // 因此在整个枚举过程中锁定集合以防止 
117         // 使用者和制造者线程修改内容 
118         // 是绝对必要的。(此方法仅由 
119         // 主线程调用。) 
120         lock (((ICollection)q).SyncRoot) 
121         { 
122             foreach (int i in q) 
123             { 
124                 Console.Write("{0} ", i); 
125             } 
126         } 
127         Console.WriteLine(); 
128     } 
129   
130     static void Main() 
131     { 
132         // 配置结构,该结构包含线程同步 
133         // 所需的事件信息。 
134         SyncEvents syncEvents = new SyncEvents(); 
135   
136         // 泛型队列集合用于存储要制造和使用的 
137         // 项。此例中使用的是“int”。 
138         Queue<int> queue = new Queue<int>(); 
139   
140         // 创建对象,一个用于制造项,一个用于 
141         // 使用项。将队列和线程同步事件传递给 
142         // 这两个对象。 
143         Console.WriteLine("Configuring worker threads..."); 
144         Producer producer = new Producer(queue, syncEvents); 
145         Consumer consumer = new Consumer(queue, syncEvents); 
146   
147         // 为制造者对象和使用者对象创建线程 
148         // 对象。此步骤并不创建或启动 
149         // 实际线程。 
150         Thread producerThread = new Thread(producer.ThreadRun); 
151         Thread consumerThread = new Thread(consumer.ThreadRun); 
152   
153         // 创建和启动两个线程。 
154         Console.WriteLine("Launching producer and consumer threads...");         
155         producerThread.Start(); 
156         consumerThread.Start(); 
157   
158         // 为制造者线程和使用者线程设置 10 秒的运行时间。 
159         // 使用主线程(执行此方法的线程) 
160         // 每隔 2.5 秒显示一次队列内容。 
161         for (int i = 0; i < 4; i++) 
162         { 
163             Thread.Sleep(2500); 
164             ShowQueueContents(queue); 
165         } 
166   
167         // 向使用者线程和制造者线程发出终止信号。 
168         // 这两个线程都会响应,由于 ExitThreadEvent 是 
169         // 手动重置的事件,因此除非显式重置,否则将保持“设置”。 
170         Console.WriteLine("Signaling threads to terminate..."); 
171         syncEvents.ExitThreadEvent.Set(); 
172   
173         // 使用 Join 阻塞主线程,首先阻塞到制造者线程 
174         // 终止,然后阻塞到使用者线程终止。 
175         Console.WriteLine("main thread waiting for threads to finish..."); 
176         producerThread.Join(); 
177         consumerThread.Join(); 
178     } 
179 }
 
本文作者:网友 来源:腾讯科技
CIO之家 www.ciozj.com 微信公众号:imciow
   
免责声明:本站转载此文章旨在分享信息,不代表对其内容的完全认同。文章来源已尽可能注明,若涉及版权问题,请及时与我们联系,我们将积极配合处理。同时,我们无法对文章内容的真实性、准确性及完整性进行完全保证,对于因文章内容而产生的任何后果,本账号不承担法律责任。转载仅出于传播目的,读者应自行对内容进行核实与判断。请谨慎参考文章信息,一切责任由读者自行承担。
延伸阅读