(转).NET 多线程编程

转自http://www.cnblogs.com/luoht/archive/2011/01/03/1924832.html
线程的基本概念

• 线程是程序执行的基本原子单位. 一个进程可以由多个线程组成.

• 每个线程都维护异常处理程序、调度优先级和一组系统用于在调度该线程前保存线程上下文的结构。线程上下文包括为使线程在线程的宿主进程地址空间中无缝地继续执行所需的所有信息,包括线程的CPU 寄存器组和堆栈。

• 在分布式编程中,正确使用线程能够很好的提高应用程序的性能及运行效率.实现原理是将一个进程分成多个线程,然后让它们并发异步执行,来提高运行效率.

• 并发执行并不是同时执行(占有CPU),任意时刻还是只能有一个线程占用CPU,只不过是它们争夺CPU频繁一些,感觉到他们似乎都在运行.

什么时候用线程?

• 一般情况下,如果多个线程在执行时都要抢占某一个资源或某几个资源,则最好不用异步线程执行.因为它们是并发执行,很可能同时争夺某个资源有CPU,这时要么执行资源分配算法(比如要判断哪个线程优先级高,这要花费时间),或者是按时间片算法(这样要付出轮询CUP/交接/让出CPU所需的时间).

• 如果多个线程所需要的系统资源是比较均匀的,这时完全可以让它们异步并发执行,

使用线程的缺点

• 统将为进程和线程所需的上下文信息使用内存。因此,可以创建的进程、AppDomain 对象和线程的数目会受到可用内存的限制。

• 跟踪大量的线程将占用大量的处理器时间。如果线程过多,则其中大多数线程都不会产生明显的进度。如果大多数当前线程处于一个进程中,则其他进程中的线程的调度频率就会很低。

• 使用许多线程控制代码执行非常复杂,并可能产生许多错误。

• 销毁线程需要了解可能发生的问题并对那些问题进行处理。

线程池

• 许多应用程序创建的线程都要在休眠状态中消耗大量时间,以等待事件发生。这样会浪费资源。

• 线程池通过为应用程序提供一个由系统管理的辅助线程池使您可以更为有效地使用线程。一个线程监视排到线程池的若干个等待操作的状态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。

• 实际上,如果要执行一些需要多个线程的较短任务,则使用ThreadPool 类是利用多个线程的最方便且最好的方法。使用线程池使系统能够不仅针对此进程而且针对计算机上的其他进程(您的应用程序对其一无所知)对此情况进行优化以达到更好的吞吐量。使用线程池使系统能够在考虑到计算机上的所有当前进程后对线程时间片进行优化。

ThreadPool

• 线程池在首次创建ThreadPool 类的实例时被创建。线程池具有每个可用处理器25 个线程的默认限制

• 可以将与等待操作不相关的工作项排列到线程池。若要请求由线程池中的一个线程来处理工作项,请调用QueueUserWorkItem 方法。此方法将对将被从线程池中选定的线程调用的方法或委托的引用用作参数。一个工作项排入队列后就无法再取消它。

 

创建和终止线程

01using System;
02using System.Threading;
03  
04public 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  
24public 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}

 

使用线程池

 

 

01using System;
02using System.Threading;
03  
04// Fibonacci 类为使用辅助
05// 线程执行长时间的 Fibonacci(N) 计算提供了一个接口。
06// N 是为 Fibonacci 构造函数提供的,此外还提供了
07// 操作完成时对象发出的事件信号。
08// 然后,可以使用 FibOfN 属性来检索结果。
09public 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  
49public 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}

 

线程同步和互交

 

001using System;
002using System.Threading;
003using System.Collections;
004using System.Collections.Generic;
005  
006// 将线程同步事件封装在此类中, 
007// 以便于将这些事件传递给 Consumer 和
008// Producer 类。
009public 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 个项。
055public 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 类。
087public 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  
111public 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}

转载于:https://www.cnblogs.com/ColdFish_Pegasus/archive/2011/01/04/1925727.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值