新思想

C# 多线程程序 源代码

C# 多线程程序 源代码

ThreadExample

一、线程启动和停止

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;
        }
    }
}

源程序下载

执行程序下载

posted on 2015-11-29 21:17  新思想  阅读(971)  评论(0)    收藏  举报

导航