C# 多线程程序 源代码
C# 多线程程序 源代码
一、线程启动和停止
using System; using System.Threading; namespace ThreadExample { public class Worker { // Volatile 用于向编译器提示此数据成员将由多个线程访问。 private volatile bool _shouldStop; // 启动线程时调用此方法。 public void DoWork() { while (!_shouldStop) { Console.WriteLine("工作子线程: 运行中..."); } Console.WriteLine("工作子线程: 恰当停止."); } public void RequestStop() { _shouldStop = true; } } class ThreadStartStop { static void Main(string[] args) { // 创建线程对象。这不会启动该线程。 Worker workerObject = new Worker(); Thread workerThread = new Thread(workerObject.DoWork); // 启动辅助线程。 workerThread.Start(); Console.WriteLine("主线程: 开始工作子线程..."); // 循环直至辅助线程激活。 while (!workerThread.IsAlive) ; // 为主线程设置 1 毫秒的休眠, // 以使辅助线程完成某项工作。 Thread.Sleep(1); // 请求辅助线程自行停止: workerObject.RequestStop(); // 使用 Join 方法阻塞当前线程, // 直至对象的线程终止。 workerThread.Join(); Console.WriteLine("主线程: 工作子线程已经停止."); Console.ReadKey(); } } }
二、线程的同步
// 参考:http://blog.csdn.net/zhoufoxcn/article/details/5170815 using System; using System.Threading; using System.Collections; using System.Collections.Generic; // 将线程同步事件封装在此类中, // 以便于将这些事件传递给 Consumer 和 Producer 类。 public class SyncEvents { private EventWaitHandle _newItemEvent; private EventWaitHandle _exitThreadEvent; private WaitHandle[] _eventArray; public SyncEvents() { // AutoResetEvent 用于“新项”事件,因为我们希望每当使用者线程响应此事件时, // 此事件就会自动重置。 _newItemEvent = new AutoResetEvent(false); // ManualResetEvent 用于“退出”事件,因为我们希望发出此事件的信号时有多个线程响应。 // 如果使用 AutoResetEvent,事件对象将在单个线程作出响应之后恢复为 // 未发信号的状态,而其他线程将无法终止。 _exitThreadEvent = new ManualResetEvent(false); // 这两个事件也放在一个 WaitHandle 数组中,以便 // 使用者线程可以使用 WaitAny 方法阻塞这两个事件。 _eventArray = new WaitHandle[2]; _eventArray[0] = _newItemEvent; _eventArray[1] = _exitThreadEvent; } // 公共属性允许对事件进行安全访问。 public EventWaitHandle ExitThreadEvent { get { return _exitThreadEvent; } } public EventWaitHandle NewItemEvent { get { return _newItemEvent; } } public WaitHandle[] EventArray { get { return _eventArray; } } } // Producer 类(使用一个辅助线程) // 将项异步添加到队列中,共添加 20 个项。 public class Producer { private Queue<int> _queue; private SyncEvents _syncEvents; public Producer(Queue<int> q, SyncEvents e) { _queue = q; _syncEvents = e; } public void ThreadRun() { int count = 0; Random r = new Random(); while (!_syncEvents.ExitThreadEvent.WaitOne(0, false)) { lock (((ICollection)_queue).SyncRoot) { while (_queue.Count < 20) { _queue.Enqueue(r.Next(0, 100)); _syncEvents.NewItemEvent.Set(); count++; } } } Console.WriteLine("生产者线程: 生产 {0} 项", count); } } // Consumer 类通过自己的辅助线程使用队列中的项。 // Producer 类使用 NewItemEvent 将新项通知 Consumer 类。 public class Consumer { private Queue<int> _queue; private SyncEvents _syncEvents; public Consumer(Queue<int> q, SyncEvents e) { _queue = q; _syncEvents = e; } public void ThreadRun() { int count = 0; while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1) { lock (((ICollection)_queue).SyncRoot) { int item = _queue.Dequeue(); } count++; } Console.WriteLine("消费者线程: 消费 {0} 项", count); } } public class ThreadSyncSample { private static void ShowQueueContents(Queue<int> q) { // 对集合进行枚举本来就不是线程安全的, // 因此在整个枚举过程中锁定集合以防止 // 使用者和制造者线程修改内容是绝对必要的。(此方法仅由主线程调用。) lock (((ICollection)q).SyncRoot) { foreach (int i in q) { Console.Write("{0} ", i); } } Console.WriteLine(); } static void Main() { // 配置结构,该结构包含线程同步所需的事件信息。 SyncEvents syncEvents = new SyncEvents(); // 泛型队列集合用于存储要制造和使用的项。 // 此例中使用的是“int”。 Queue<int> queue = new Queue<int>(); // 创建对象,一个用于制造项,一个用于使用项。 // 将队列和线程同步事件传递给这两个对象。 Console.WriteLine("配置工作子线程中..."); Producer producer = new Producer(queue, syncEvents); Consumer consumer = new Consumer(queue, syncEvents); // 为制造者对象和使用者对象创建线程对象。 // 此步骤并不创建或启动实际线程。 Thread producerThread = new Thread(producer.ThreadRun); Thread consumerThread = new Thread(consumer.ThreadRun); // 创建和启动两个线程。 Console.WriteLine("执行生产者与消费者线程..."); producerThread.Start(); consumerThread.Start(); // 为制造者线程和使用者线程设置 10 秒的运行时间。 // 使用主线程(执行此方法的线程)每隔 2.5 秒显示一次队列内容。 for (int i = 0; i < 4; i++) { Thread.Sleep(2500); ShowQueueContents(queue); } // 向使用者线程和制造者线程发出终止信号。 // 这两个线程都会响应,由于 ExitThreadEvent 是 // 手动重置的事件,因此除非显式重置,否则将保持“设置”。 Console.WriteLine("通知子线程终止..."); syncEvents.ExitThreadEvent.Set(); // 使用 Join 阻塞主线程,首先阻塞到制造者线程 // 终止,然后阻塞到使用者线程终止。 Console.WriteLine("主线程等待子线程终止..."); producerThread.Join(); consumerThread.Join(); Console.ReadKey(); } }
三、线程的参数
// 如果要在实例化线程时要带一些参数,就不能用ThreadStart委托作为构造函数的参数来实例化Thread了,而要ParameterizedThreadStart委托,它在实例化时可以用一个带有一个Object参数的方法作为构造函数的参数,而实例化ThreadStart时所用到的方法是没有参数的。 // 参考: http://blog.csdn.net/zhoufoxcn/article/details/4402999 using System; using System.Threading; namespace StartThread { class MyThreadParameter { private int interval; private int loopCount; /// <summary> /// 循环次数 /// </summary> public int LoopCount { get { return loopCount; } } /// <summary> /// 线程的暂停间隔 /// </summary> public int Interval { get { return interval; } } /// <summary> /// 构造函数 /// </summary> /// <param name="interval">线程的暂停间隔</param> /// <param name="loopCount">循环次数</param> public MyThreadParameter(int interval, int loopCount) { this.interval = interval; this.loopCount = loopCount; } } class Program { int interval = 200; static void Main(string[] args) { Program p = new Program(); Thread parameterThread = new Thread(new ParameterizedThreadStart(p.MyParameterRun)); parameterThread.Name = "Thread A:"; MyThreadParameter paramter = new MyThreadParameter(50, 20); parameterThread.Start(paramter); Console.ReadKey(); } /// <summary> /// 带多个参数的启动方法 /// </summary> /// <param name="ms">方法参数</param> public void MyParameterRun(object ms) { MyThreadParameter parameter = ms as MyThreadParameter;//类型转换 if (parameter != null) { for (int i = 0; i < parameter.LoopCount; i++) { Console.WriteLine(Thread.CurrentThread.Name + "系统当前时间毫秒值:" + DateTime.Now.Millisecond.ToString()); Thread.Sleep(parameter.Interval);//让线程暂停 } } } } }
四、跨线程访问 UI 控件
// 在.NET中出于线程安全的考虑,不允许在调试环境下使用线程访问并非它自己创建的UI控件。 // 这可以用比较简单的方法解决,那就是设置CheckForIllegalCrossThreadCalls这个静态属性,它默认是true,如果将其设为false的话,以后在多线程环境下操作界面也不会抛出异常了。 // 参考:http://blog.csdn.net/zhoufoxcn/article/details/5205690 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Threading; namespace ThreadandUI { public partial class Form1 : Form { // 定义delegate以便Invoke时使用 private delegate void SetProgressBarValue(int value); public Form1() { InitializeComponent(); } private void btnThread_Click(object sender, EventArgs e) { progressBar.Value = 0; //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程 CheckForIllegalCrossThreadCalls = false; Thread thread = new Thread(new ThreadStart(Run)); thread.Start(); } //使用线程来直接设置进度条 private void Run() { while (progressBar.Value < progressBar.Maximum) progressBar.Value++; //progressBar.PerformStep(); } private void btnInvoke_Click(object sender, EventArgs e) { progressBar.Value = 0; CheckForIllegalCrossThreadCalls = true; Thread thread = new Thread(new ThreadStart(RunWithInvoke)); thread.Start(); } //使用Invoke方法来设置进度条 private void RunWithInvoke() { int value = progressBar.Value; while (value < progressBar.Maximum) { //如果是跨线程调用 if (InvokeRequired) this.Invoke(new SetProgressBarValue(SetProgressValue), ++value); else progressBar.Value = ++value; } } //跟SetProgressBarValue委托相匹配的方法 private void SetProgressValue(int value) { progressBar.Value = value; } } }
浙公网安备 33010602011771号