任务同步五

Events类:

与互斥和信号量对象一样,事件也是一个系统范围内的资源同步方法。为了从托管代码中使用系统事件,.net framework 在system.Threading名称空间中提供了ManualResetEvent、AutoResetEvent、ManualResetEventSlim和CountdownEvent类。

 AutoResetEvent(bool initialState):构造函数,用一个指示是否将初始状态设置为终止的布尔值初始化该类的新实例。

        false:无信号,子线程的WaitOne方法不会被自动调用

        true:有信号,子线程的WaitOne方法会被自动调用

   Reset ():将事件状态设置为非终止状态,导致线程阻止;如果该操作成功,则返回true;否则,返回false。

   Set ():将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false。

   WaitOne(): 阻止当前线程,直到收到信号。

   WaitOne(TimeSpan, Boolean) :阻止当前线程,直到当前实例收到信号,使用 TimeSpan 度量时间间隔并指定是否在等待之前退出同步域。   

     WaitAll():等待全部信号。

参考案例:

    public class Request
    {
        //建立事件数组
        public AutoResetEvent[] autoEvents = null;
        public Request()
        {
            autoEvents = new AutoResetEvent[] {
                new AutoResetEvent(false),
                new AutoResetEvent(false)
            };
        }
        public string MethodOne()
        {
            Console.WriteLine("执行方法一");
            Task.Delay(2000).Wait();
            //有信号
            autoEvents[0].Set();
            return "方法一执行完成";
        }
        public string MethodTwo()
        {
            Console.WriteLine("执行方法二");
            Task.Delay(1000).Wait();
            //有信号
            autoEvents[1].Set();
            return "方法二执行完成";
        }
        public void MethodThread(Task<string>[] t)
        {
            Console.WriteLine($"{t[0].Result}、{t[1].Result},准备执行第三个方法");

        }
    }
//调用
            Request req = new Request();
            Task<string> t1 = Task.Run(() => { return req.MethodOne(); });
            Task<string> t2 = new Task<string>(req.MethodTwo);
            t2.Start();
            WaitHandle.WaitAll(req.autoEvents);
            req.MethodThread(new Task<string>[] { t1, t2 });
 //超市购物排队付款等待收银员通知付款
 //付钱方法
        static void pay()
        {
            string tip = Console.ReadLine();
            if (tip == "next")
            {
                AutoReset.Set();//收银员发送付款通知
            }
        }
//调用方式
            var t1 = Task.Run(() =>
            {
                Console.WriteLine("客户1等待付钱");
                AutoReset.WaitOne();
                Console.WriteLine("客户1付钱");
                AutoReset.Set();
            });
            var t2 = Task.Run(() =>
            {
                Console.WriteLine("客户2等待付钱");
                AutoReset.WaitOne(3000);
                Console.WriteLine("客户2付钱");
            });

            pay();//发送通知
            Console.ReadLine();

通过上述案例可以看出调用Set()方法向AutoResetEvent发信号。也可以使用Reset()方法使之返回不发信号的状态。但是,如果一线程在等待自动重置的事件发信号。当第一个线程的等待状态结束时,该事件会自动变为不发信号的状态。这样,如果多个线程在等待向事件发信号,就只有一个线程结束其等待状态,它不是等待时间最长的线程,而是优先级最高的线程。

ManualResetEvent类的使用

static ManualResetEvent AutoReset = new ManualResetEvent(false);
       //付钱方法
        static void pay()
        {
            AutoReset.Set();//收银员发送付款通知
        }
            var t1 = Task.Run(() =>
            {
                Console.WriteLine("客户1直接付钱");
                AutoReset.WaitOne();
                Console.WriteLine("客户1已经付钱");
            });
            var t2 = Task.Run(() =>
            {
                Console.WriteLine("客户2直接付钱");
                AutoReset.WaitOne();
                Console.WriteLine("客户2已经付钱");
            });
            pay();//发送通知
            Console.ReadLine();

ManualResetEvent类的使用方法大体和上述的AutoResetEvent类似;主要区别是:AutoResetEvent一次只能通知一个等待任务,通知完成后自动关闭;ManualResetEvent一次可以通知多个等待的任务,但是要关闭需要手动调用reset()方法

1.ManualResetEvent 调用一次Set()后将允许恢复所有被阻塞线程。需手动在调用WaitOne()之后调用Reset()重置信号量状态为非终止,然后再次调用WaitOne()的时候才能继续阻塞线程,反之则不阻塞

2.AutoResetEvent,调用一次Set()只能继续被阻塞的一个线程,多次调用Set()才行,但不需手动调用Reset();再次调用WaitOne()的时候又能阻塞线程,也是和前者的区别。

ManualResetEventSlim类是ManualResetEvent的简化版

    public class Calculator
    {
        private ManualResetEventSlim _mEvent;
        public Calculator(ManualResetEventSlim mEvent)
        {
            _mEvent = mEvent;
        }
        public int Result { get; set; }
        public void Calculation(int x, int y)
        {
            Console.WriteLine($"任务{Task.CurrentId}开始计算");
            Task.Delay(new Random().Next(3000)).Wait();
            Result = x + y;
            Console.WriteLine($"任务{Task.CurrentId}准备好了");
            _mEvent.Set();
        }
    }
        static void Main(string[] args)
        {
            var taskCount = 4;
            var mEvents = new ManualResetEventSlim[taskCount];
            var waitHandles = new WaitHandle[taskCount];
            var calcs = new Calculator[taskCount];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Task[] tasks = new Task[taskCount];
            for (int i = 0; i < tasks.Length; i++)
            {
                int il = i;
                mEvents[i] = new ManualResetEventSlim(false);
                waitHandles[i] = mEvents[i].WaitHandle;
                calcs[i] = new Calculator(mEvents[i]);
                Task.Run(() =>
                {
                    calcs[il].Calculation(il + 1, il + 3);
                });
            }
            for (int i = 0; i < taskCount; i++)
            {
                int index = WaitHandle.WaitAny(waitHandles);
                if (index == WaitHandle.WaitTimeout)
                {
                    Console.WriteLine("Timeout");
                }
                else
                {
                    mEvents[index].Reset();
                    Console.WriteLine($"完成任务{index},result:{calcs[index].Result}");
                }
            }
            sw.Stop();
            var times = sw.Elapsed.TotalSeconds;
            Console.WriteLine("所消耗时间:" + times);
            Console.ReadLine();
        }

CountdownEvent类:在一个类似的场景中,为了把一些工作分支到多个任务中,并在以后合并结果,使用新的CountdownEvent类很有用。不需要为每个任务创建一个单独的事件对象,而只需要创建一个事件对象,CountdownEvent类为所有设置了事件的任务定义一个初始数字,在到达该计数后,就向CountdownEvent类发信号。

    public class Calculator
    {
        private CountdownEvent _cEvent;
        public Calculator(CountdownEvent cEvent)
        {
            _cEvent = cEvent;
        }
        public int Result { get; set; }
        public void Calculation(Tuple<int, int> tuple)
        {
            Console.WriteLine($"任务{Task.CurrentId}开始计算");
            Task.Delay(new Random().Next(3000)).Wait();
            Result = tuple.Item1 + tuple.Item2;
            Console.WriteLine($"任务{Task.CurrentId}准备好了");
            _cEvent.Signal();
        }
    }
        static void Main(string[] args)
        {
            var taskCount = 4;
            var cEvent = new CountdownEvent(4);
            var waitHandles = new WaitHandle[taskCount];
            var calcs = new Calculator[taskCount];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Task[] tasks = new Task[taskCount];
            for (int i = 0; i < taskCount; i++)
            {
                calcs[i] = new Calculator(cEvent);
                int il = i;
                Task.Run(() => calcs[il].Calculation(Tuple.Create(il + 1, il + 3)));
            }
            cEvent.Wait();
            Console.WriteLine("全部完成");
            for (int i = 0; i < taskCount; i++)
            {
                Console.WriteLine($"任务{i},结果:{calcs[i].Result}");
            }
            sw.Stop();
            var times = sw.Elapsed.TotalSeconds;
            Console.WriteLine("所消耗时间:" + times);
            Console.ReadLine();
        }

events类相关的同步处理方式到此告一段落

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双叶红于二月花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值