zoukankan      html  css  js  c++  java
  • C# 线程会合实例

    有这样一个题目:四个线程t1,t2,t3,t4,向4个文件中写入数据,要求:t1只能写入“1”,t2只能写入“2”,t3只能写入“3”,t4只能写入“4”,对4个文件A,B,C,D写入如下内容:

    • A:123412341234.....
    • B:234123412341....
    • C:341234123412....
    • D:412341234123....

    简单分析一下,对于A文件,t1写入“1”后,我们希望通知t2来写“2”,并且t1前往D文件等着去写“1”,以此类推。

    1. 通过等待句柄实现

    显然可以用等待句柄来实现,通知t2来写“2”相当于在一个等待句柄上调用 Set 方法,等待在D文件上写“1”相当于在另一等待句柄上调用了 WaitOne 方法,下边是利用4个 AutoResetEvent 来实现它:

    class Program
    {
        private static List<StreamWriter> _sws;
    
        private static List<AutoResetEvent> _whs;

    private static void Main(string[] args) { var fileNames = new List<string> {"A", "B", "C", "D"}; // 创建或清空文件 fileNames.ForEach(name => { if (!File.Exists(name)) File.Create(name).Close(); else { using (var sw = new StreamWriter(name)) sw.Write(""); } }); _sws = fileNames.Select(File.AppendText).ToList(); // 为每个文件写入建立一个等待句柄 _whs = fileNames.Select(name => new AutoResetEvent(false)).ToList(); // 创建并启4个线程执行任务 var threads = new List<Thread> { new Thread(() => Work(1)), new Thread(() => Work(2)), new Thread(() => Work(3)), new Thread(() => Work(4)) }; threads.ForEach(t => t.Start()); // 等待线程结束并关闭 StreamWrite threads.ForEach(t => t.Join()); Console.WriteLine("任务完成!"); _sws.ForEach(sw => sw.Close()); } static void Work(int threadIndex) { var next = threadIndex - 1; // 为让程序能结束,就打印100次 for (int i = 0; i < 100; i++) { var wh = _whs[next]; var sw = _sws[next]; lock (sw) { sw.Write(threadIndex); } next = (next - 1) < 0 ? 3 : next - 1; WaitHandle.SignalAndWait(wh, _whs[next]); //在wh上发信号,并在下一个等待句柄上等待执行 } } }

    上述例子中我们创建了4个线程来分别打印1,2,3,4,并且为每个文件的写入创建了4个等待句柄来进行信号通信。最后主线程在等待所有线程结束后,关闭文件流。为让程序能正常结束,在 Work 方法中就只循环写100次。

    以t1(列表中第一个线程)为例,在A文件中打印1后,调用 WaitHandle.SignalAndWait(wh,wh[next]),即在wh上发信号通知可以接着写入了,并在下一个等待句柄上等待写入信号。

    关于 SinalAndWait 的可以参见 Thread in C# 或者对应的 中文翻译 。

    2. 通过 Barrier 类实现

    除了通过等待句柄可以实现题目要求外,同样可以通过 WaitPulse 来实现。如果是FrameWork 4.0或更高的版本,可以通过 Barrier 类(它是建立在 Wait / Pulse 和自旋锁基础上的)更简单的实现这个题目。

    class Program
    {
        private static Barrier _barrier;
    
        private static void Main(string[] args)
        {
            var fileNames = new List<string> { "A", "B", "C", "D" };
    
            // 创建或清空文件
            fileNames.ForEach(name =>
            {
                if (!File.Exists(name))
                    File.Create(name).Close();
                else
                {
                    using (var sw = new StreamWriter(name))
                        sw.Write("");
                }
            });
            
            // 在_barrier上调用SignalAndWait的线程会被阻塞直到这个方法被调用4次
            _barrier = new Barrier(4);
    
            _sws = fileNames.Select(File.AppendText).ToList();
    
            // 创建并启4个线程执行任务
            var threads = new List<Thread> {
                new Thread(() => Work(1)), new Thread(() => Work(2)), new Thread(() => Work(3)), new Thread(() => Work(4))
            };
            threads.ForEach(t => t.Start());
    
            // 等待线程结束并关闭 StreamWrite
            threads.ForEach(t => t.Join());
            Console.WriteLine("任务完成!");
            _sws.ForEach(sw => sw.Close());
        }
    
        static void Work(int threadIndex)
        {
            var next = threadIndex - 1;
            for (int i = 0; i < 100; i++)
            {
                var sw = _sws[next];
                lock (sw)
                {
                    sw.Write(threadIndex);
                }
                _barrier.SignalAndWait();
                next = (next - 1) < 0 ? 3 : next - 1;
            }
        }
    }

    使用了一个 Barrier 类来替代4个等待句柄,线程调用 SignalAndWait 后会阻塞,直到这个方法被调用4次。在这个例子中意味着4个线程总是在同步进行着打印,下图可以很好的解释 Barrier 类:

     关于 Barrier 类,可以参见 Thread in C# 或者对应的 中文翻译 。

  • 相关阅读:
    关于不重启Tomcat自动加载改变的class文件
    Oracle数据库查询优化方案
    NavBarControl
    Spring Boot 热部署
    JSR303定义的校验类型
    C# JSON的序列化与反序列化
    常用正则表达式大全——包括校验数字、字符、一些特殊的需求等等
    什么是窗口句柄
    luffy前台配置
    luffy后台相关设置
  • 原文地址:https://www.cnblogs.com/samgk/p/4784652.html
Copyright © 2011-2022 走看看