C# 并行编程 之 轻量级手动重置事件的使用

目录(?)[-]

  1. 简单介绍
  2. 使用超时和取消
  3. 跨进程或AppDomain的同步

简单介绍

如果预计操作的等待的时间非常短,可以考虑使用轻量级的手动重置事件,ManualResetEventSlim。它可以发出信号和等待事件。从名称和使用方式上看,它主要是提供以人为本的操作方式,在基于人对程序运行过程非常了解的情况下,由人控制整个同步的过程。

ManualResetEventSlim 提供了3个常用的方法和3个只读的属性。

构造函数:

  • ManualResetEventSlim():使用无信号初始状态初始化 ManualResetEventSlim 类的新实例。
  • ManualResetEventSlim(Boolean):使用一个指示是否将初始状态设置为有信号的布尔值初始化 
    ManualResetEventSlim 类的新实例。
  • ManualResetEventSlim(Boolean, Int32):使用一个指示是否将初始状态设置为有信号和指定自旋计数的布尔值初始化 ManualResetEventSlim 类的新实例。

方法:

  • Reset:将事件置为false (取消设置/取消信号)
  • Set:将事件置为true(设置/发出信号),如果有任务在等待,这时它会得到这个信号并解除阻塞。
  • Wait:阻塞当前任务或线程,直到另外的线程发出信号。

属性:

  • IsSet:一个bool值,表明事件是否被设置。
  • SpinCount:进入内核等待前要执行自旋的次数。
  • WaitHandle:提供了操作系统对象WaitHandle的访问。通过这个对象可以等待对共享资源的排他访问。

程序示例:在这个例子中使用了ManualResetEventSlim,它使得Task1,2,3变成了顺序运行。

using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Sample5_7_manualreseteventslim
{
    class Program
    {
        private static int _TaskNum = 3;
        private static Task[] _Tasks;
        private static StringBuilder _StrBlder;
        private const int RUN_LOOP = 10;

        private static ManualResetEventSlim m_Worker2Event;
        private static ManualResetEventSlim m_Worker3Event;

        private static void Work1(int TaskID)
        {
            int i = 0;
            string log = "";

            while (i < RUN_LOOP)
            {
                log = String.Format("Time: {0}  Task : #{1}  Value: {2}  =====\n",
                                  DateTime.Now.TimeOfDay, TaskID, i);
                i++;
                try
                {
                    _StrBlder.Append(log);
                }
                finally
                {
                    m_Worker2Event.Set();
                }
            }
        }

        private static void Work2(int TaskID)
        {
            int i = 0;
            string log = "";

            m_Worker2Event.Wait();

            while ((i < RUN_LOOP) && (m_Worker2Event.IsSet))
            {
                log = String.Format("Time: {0}  Task : #{1}  Value: {2}  *****\n",
                                  DateTime.Now.TimeOfDay, TaskID, i);
                i++;
                try
                {
                    _StrBlder.Append(log);
                }
                finally
                {
                    m_Worker3Event.Set();
                }
            }
        }

        private static void Work3(int TaskID)
        {
            int i = 0;
            string log = "";

            m_Worker3Event.Wait();

            while ((i < RUN_LOOP) && (m_Worker3Event.IsSet))
            {
                log = String.Format("Time: {0}  Task : #{1}  Value: {2}  ~~~~~\n",
                                  DateTime.Now.TimeOfDay, TaskID, i);
                i++;
                try
                {
                    _StrBlder.Append(log);
                }
                finally
                {
                }
            }
        }

        static void Main(string[] args)
        {
            _Tasks = new Task[_TaskNum];
            _StrBlder = new StringBuilder();
            m_Worker2Event = new ManualResetEventSlim(false, 100);
            m_Worker3Event = new ManualResetEventSlim(false, 100);

            _Tasks[0] = Task.Factory.StartNew((num) =>
            {
                var taskid = (int)num;
                Work1(taskid);
            }, 0);

            _Tasks[1] = Task.Factory.StartNew((num) =>
            {
                var taskid = (int)num;
                Work2(taskid);
            }, 1);

            _Tasks[2] = Task.Factory.StartNew((num) =>
            {
                var taskid = (int)num;
                Work3(taskid);
            }, 2);

            var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
            {
                Task.WaitAll(_Tasks);
                Console.WriteLine("==========================================================");
                Console.WriteLine("All Phase is completed");
                Console.WriteLine("==========================================================");
                Console.WriteLine(_StrBlder);
            });

            try
            {
                finalTask.Wait();
            }
            catch (AggregateException aex)
            {
                Console.WriteLine("Task failed And Canceled" + aex.ToString());
            }
            finally
            {
                m_Worker2Event.Dispose();
                m_Worker3Event.Dispose();
            }
            Console.ReadLine();
        }

    }
}

使用超时和取消

超时机制对于任务的同步是非常必要的,这里也提供了用户设置超时的方法。 
ManualResetEventSilm.Wait(int TIME_OUT);

程序示例:在这个例子中任务1会等待5秒后在设置Event,但任务2,3的超时时间为2秒。

using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Sample5_7_manualreseteventslim
{
    class Program
    {
        private static int _TaskNum = 3;
        private static Task[] _Tasks;
        private static StringBuilder _StrBlder;
        private const int RUN_LOOP = 10;

        private static ManualResetEventSlim m_Worker2Event;
        private static ManualResetEventSlim m_Worker3Event;

        private static void Work1(int TaskID)
        {
            int i = 0;
            string log = "";

            while (i < RUN_LOOP)
            {
                log = String.Format("Time: {0}  Task : #{1}  Value: {2}  =====\n",
                                  DateTime.Now.TimeOfDay, TaskID, i);
                i++;
                try
                {
                    _StrBlder.Append(log);
                }
                finally
                {
                    System.Threading.Thread.Sleep(5000);
                    m_Worker2Event.Set();
                }
            }
        }

        private static void Work2(int TaskID)
        {
            int i = 0;
            string log = "";

            if (!m_Worker2Event.Wait(2000))
            {
                Console.WriteLine("Task 2 wait for event TIME OUT!!");
                return;
            }

            while ((i < RUN_LOOP) && (m_Worker2Event.IsSet))
            {
                log = String.Format("Time: {0}  Task : #{1}  Value: {2}  *****\n",
                                  DateTime.Now.TimeOfDay, TaskID, i);
                i++;
                try
                {
                    _StrBlder.Append(log);
                }
                finally
                {
                    m_Worker3Event.Set();
                }
            }
        }

        private static void Work3(int TaskID)
        {
            int i = 0;
            string log = "";

            if (!m_Worker3Event.Wait(2000))
            {
                Console.WriteLine("Task 3 wait for event TIME OUT!!");
                return;
            }

            while ((i < RUN_LOOP) && (m_Worker3Event.IsSet))
            {
                log = String.Format("Time: {0}  Task : #{1}  Value: {2}  ~~~~~\n",
                                  DateTime.Now.TimeOfDay, TaskID, i);
                i++;
                try
                {
                    _StrBlder.Append(log);
                }
                finally
                {
                }
            }
        }

        static void Main(string[] args)
        {
            _Tasks = new Task[_TaskNum];
            _StrBlder = new StringBuilder();
            m_Worker2Event = new ManualResetEventSlim(false, 100);
            m_Worker3Event = new ManualResetEventSlim(false, 100);

            _Tasks[0] = Task.Factory.StartNew((num) =>
            {
                var taskid = (int)num;
                Work1(taskid);
            }, 0);

            _Tasks[1] = Task.Factory.StartNew((num) =>
            {
                var taskid = (int)num;
                Work2(taskid);
            }, 1);

            _Tasks[2] = Task.Factory.StartNew((num) =>
            {
                var taskid = (int)num;
                Work3(taskid);
            }, 2);

            var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
            {
                Task.WaitAll(_Tasks);
                Console.WriteLine("==========================================================");
                Console.WriteLine("All Phase is completed");
                Console.WriteLine("==========================================================");
                Console.WriteLine(_StrBlder);
            });

            try
            {
                finalTask.Wait();
            }
            catch (AggregateException aex)
            {
                Console.WriteLine("Task failed And Canceled" + aex.ToString());
            }
            finally
            {
                m_Worker2Event.Dispose();
                m_Worker3Event.Dispose();
            }
            Console.ReadLine();
        }

    }
}

可以注意到其实Task 1只是阻塞了Task2,但Task3也受到了超时的影响。超时机制在系统中的传播会对整个程序造成一定的影响。

好的方面:帮助系统所有模块了解到系统中出现了某些异常,要采取措施了。 
坏的方面:有些模块对于其他模块的超时并没有相应的准备,直接导致一连串的异常的反应,系统崩溃。

跨进程或AppDomain的同步

如果需要实现跨进程的同步,这时可以使用ManualResetEvent。ManualResetEvent和ManualResetEventSlim有一定的区别,比如它没有IsSet属性。

方法:

  • Reset:将事件置为false (取消设置/取消信号)
  • Set:将事件置为true(设置/发出信号),如果有任务在等待,这时它会得到这个信号并解除阻塞。
  • WaitOne:阻塞当前任务或线程,直到另外的线程发出信号。

ManualResetEvent的使用方式和ManualResetEventSlim还是基本一致的。

时间: 2024-10-15 01:55:35

C# 并行编程 之 轻量级手动重置事件的使用的相关文章

C#线程同步手动重置事件——ManualResetEvent

和AutoResetEvent类的区别是,Manual一旦set后不会自动reset,会放行所有waitone的线程,而autoresetevent每一次set之后只会放行一个waitone的线程,然后立即自动reset.这就是自动和手动的区别,实际上说的是自动reset和手动reset. 这里就贴一段翻译注释的msdn官方代码吧. using System; using System.Collections.Generic; using System.Linq; using System.Te

C#并行编程 (Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait )

背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要访问共享的数据和资源的时候,您必须添加显示的同步,或者使用原子操作或锁. 之前的.NET Framework提供了昂贵的锁机制以及遗留的多线程模型,新的数据结构允许细粒度的并发和并行化,并且降低一定必要的开销,这些数据结构称为轻量级同步原语. 这些数据结构在关键场合下能够提供更好的性能,因为它们能够

C#并行编程-线程同步原语(Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait,Monitor,volatile)

菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要访问共享的数据和资源的时候,您必须添加显示的同步,或者使用原子操作或锁. 之前的.NET Framework提供了昂贵的锁机制以及遗留的多线程模型,新的数据结构允许细粒度的并发和并行化,并且降低一定必要的开销,这些数据结

C#并行编程-线程同步原语

原文:C#并行编程-线程同步原语 菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要访问共享的数据和资源的时候,您必须添加显示的同步,或者使用原子操作或锁. 之前的.NET Framework提供了昂贵的锁机制以及遗留的多线程模型,新的数据结构允许细粒度的并发和并行化,

C#并行编程(6):线程同步面面观

理解线程同步 线程的数据访问 在并行(多线程)环境中,不可避免地会存在多个线程同时访问某个数据的情况.多个线程对共享数据的访问有下面3种情形: 多个线程同时读取数据: 单个线程更新数据,此时其他线程读取数据: 多个线程同时更新数据. 显而易见,多个线程同时读取数据是不会产生任何问题的.仅有一个线程更新数据的时候,貌似也没有问题,但真的没有问题吗?多个线程同时更新数据,很明显,你可能把我的更改覆盖掉了,数据从此不再可信. 什么是线程同步 为了解决多线程同时访问共享数据可能导致数据被破坏的问题,我们

C#中的多线程 - 并行编程 z

原文:http://www.albahari.com/threading/part5.aspx 专题:C#中的多线程 1并行编程Permalink 在这一部分,我们讨论 Framework 4.0 加入的多线程 API,它们可以充分利用多核处理器. 并行 LINQ(Parallel LINQ)或称为 PLINQ Parallel类 任务并行(task parallelism)构造 SpinLock 和 SpinWait 这些 API 可以统称为 PFX(Parallel Framework,并行

并行计算复习————第四篇 并行计算软件支撑:并行编程

并行计算复习 第四篇 并行计算软件支撑:并行编程 Ch13 并行程序设计基础 13.1并行语言构造方法 库例程:MPI.Pthreads 扩展串行语言:Fortran90 加编译注释构造:OpenMP 13.2并行性问题 可利用SPMD来伪造MPMD 需要运行MPMD:parbegin S1 S2 S3 parend 可以改造成SPMD: for i = 1 to 3 par-do if i == 1 then S1 else if i == 2 then S2 else if i == 3 t

并行编程框架 ForkJoin

本文假设您已经了解一般并行编程知识,了解Java concurrent部分如ExecutorService等相关内容. 虽说是Java的ForkJoin并行框架,但不要太在意Java,其中的思想在其它语言环境也是同样适用的.因为并发编程在本质上是一样的.就好像如何找到优秀的Ruby程序员?其实要找的只是一个优秀的程序员.当然,如果语言层面直接支持相关的语义会更好. 引言 Java 语言从一开始就支持线程和并发性语义.Java5增加的并发工具又解决了一般应用程序的并发需求,Java6.Java7又

Python3 系列之 并行编程

进程和线程 进程是程序运行的实例.一个进程里面可以包含多个线程,因此同一进程下的多个线程之间可以共享线程内的所有资源,它是操作系统动态运行的基本单元:每一个线程是进程下的一个实例,可以动态调度和独立运行,由于线程和进程有很多类似的特点,因此,线程又被称为轻量级的进程.线程的运行在进程之下,进程的存在依赖于线程: 开胃菜 基于 Python3 创建一个简单的进程示例 from threading import Thread from time import sleep class CookBook