多核时代 .NET Framework 4 中的并行编程8---任务的同步

简介:

在并行编程过程中,多个任务同时执行时,就会涉及到任务的同步问题..Net为我们提供很多解决任务同步的类和方法.下面在具体介绍.当然,这些类和方法也适用于处理多线程(Thread)编程的同步问题.

1. Barrier

Barrier类是.Net4中新增加一个类, 它使多个任务能够采用并行方式依据某种算法在多个阶段中协同工作。例如:

           BankAccount[] accounts = new BankAccount[5];

            for (int i = 0; i < accounts.Length; i++)

            {

                accounts[i] = new BankAccount();

            }

 

            int totalBalance = 0;

 

            System.Threading.Barrier barrier = newSystem.Threading.Barrier(5, (myBarrier) =>

            {

                totalBalance = 0;

 

                foreach (var account in accounts)

                {

                    totalBalance += account.Balance;

                }

 

                Console.WriteLine("Total balance:{0}", totalBalance);

            });

 

            Task[] tasks = new Task[5];

            for (int i = 0; i < tasks.Length; i++)

            {

                tasks[i] = new Task((indata) =>

                {

                    BankAccount account = (BankAccount)indata;

 

                    Random rnd = new Random();

                    for (int j = 0; j < 1000; j++)

                    {

                        account.Balance += rnd.Next(1, 100);

                    }

                    Console.WriteLine("Task {0},phase {1} ended",Task.CurrentId, barrier.CurrentPhaseNumber);

                    barrier.SignalAndWait();

 

                    account.Balance -= (totalBalance - account.Balance) / 10;

 

                    Console.WriteLine("Task {0},phase {1} ended",Task.CurrentId, barrier.CurrentPhaseNumber);

 

                    barrier.SignalAndWait();

                }, accounts[i]);

            }

 

            foreach (var t in tasks)

            {

                t.Start();

            }

 

            Task.WaitAll(tasks);

 

            Console.WriteLine("Press enter to finish");

            Console.ReadLine();

通过上面的代码,我们可以知道:

初始化一个Barrier实例,我们可以使用Barrier(Int32, Action<Barrier>)来实例,它的第一个参数表示参数同步线程的数量(也就是将有多少个线程参与进来), Action则表示所有参与者线程到达一个阶段的屏障之后,就会执行Action委托中的代码.如果没有Action委托,则可以传递null,此时就是Barrier(Int32)构造函数了.当参与的线程到达某个阶段时就可以调用Barrier的SignalAndWait();方法, 发出参与者已达到 Barrier 的信号,并等待所有其他参与者也达到屏障.

通俗的讲,Barrier的原理就好比110米栏,刘翔和罗伯斯比赛,当刘翔先跨过第1个栏时就会调用SignalAndWait()方法,告诉大家我已经过了第1个拦,然后等待罗伯斯也跨过第1个栏,当所有人都过了第1个栏,就会调用初始化Barrier时的是Action委托.接着,后续都是这么一个过程.在比赛过程中,如果有其他人加入,则可以调用AddParticipant()方法,如果中途有人退出比赛了,则可以调用RemoveParticpant()退出.

2. CountdownEvent

表示在计数变为零时处于有信号状态的同步基元。其原理是通过一个计数,来监视当前参与者的数量,当某个任务完成后,可以调用CountdownEvent的 Signal()方法表示发出信号,通知任务完成并减少计数.此外,可以调用CountdownEvent 的Wait()方法则阻塞线程直到设置了 CountdownEvent 为止(就是计数为0,也就是所有的参与者都完成了).代码如下:

            CountdownEvent cd = new CountdownEvent(5);

            Random rnd = new Random();

            Task[] tasks = new Task[6];

            for (int i = 0; i < tasks.Length; i++)

            {

                tasks[i] = new Task(() =>

                {

                    Thread.Sleep(rnd.Next(500, 1000));

                    Console.WriteLine("Task {0} signalling event",Task.CurrentId);

                    cd.Signal();

                });

            }

 

            tasks[5] = new Task(() =>

            {

                cd.Wait();

                Console.WriteLine("Event has been set");

            });

            foreach (Task t in tasks)

            {

                t.Start();

            }

            Task.WaitAll(tasks);

            Console.WriteLine("Press enter to finish");

            Console.ReadLine();

3. ManualResetEventSlim

它是 ManualResetEvent 的简化版本,轻量级的实现. 其Reset方法将事件状态设置为非终止状态,从而导致线程受阻。 其Set 方法将事件状态设置为有信号,从而允许一个或多个等待该事件的线程继续。 其 Wait()方法 阻止当前线程,直到设置了当前 ManualResetEventSlim 为止。代码如下:

ManualResetEventSlim manualResetEvent = newManualResetEventSlim();

 

            CancellationTokenSource tokenSource = newCancellationTokenSource();

 

            Task waitingTask = Task.Factory.StartNew(() =>

            {

                while (true)

                {

                    manualResetEvent.Wait(tokenSource.Token);

                    Console.WriteLine("Waiting task active");

 

                }

            }, tokenSource.Token);

 

            Task signallingTask = Task.Factory.StartNew(() =>

            {

                Random rnd = new Random();

                while (!tokenSource.Token.IsCancellationRequested)

                {

                    tokenSource.Token.WaitHandle.WaitOne(rnd.Next(500, 2000));

                    manualResetEvent.Set();

                    Console.WriteLine("Event Set");

 

                    tokenSource.Token.WaitHandle.WaitOne(rnd.Next(500, 2000));

                    manualResetEvent.Reset();

                    Console.WriteLine("Event ReSet");

                }

            });

 

 

            Console.WriteLine("Press enter to cancel tasks");

            Console.ReadLine();

 

            tokenSource.Cancel();

            try

            {

                Task.WaitAll(waitingTask, signallingTask);

            }

            catch (AggregateException ex)

            {

                ex.Flatten().Handle((inner) =>

                {

                    Console.WriteLine("Exception is {0}", inner.Message);

                    return true;

 

                });

            }

 

            Console.WriteLine("Press enter to finish");

            Console.ReadLine();

 .NET Framework 4 版中,当等待时间预计非常短时,并且当事件不会跨越进程边界时,可使用 System.Threading.ManualResetEventSlim 类以获得更好的性能。当等待事件变为已发出信号状态的过程中,ManualResetEventSlim 短时间内会使用繁忙旋转。 当等待时间很短时,旋转的开销相对于使用等待句柄来进行等待的开销会少很多。但是,如果事件在某个时间段内没有变为已发出信号状态,则 ManualResetEventSlim 会采用常规的事件处理等待。

4. AutoResetEvent

它和ManualResetEventSlim类似,AutoResetEvent 类表示一个本地等待处理事件,在释放了单个等待线程以后,该事件会在终止时自动重置。代码如下:

   var arEvent = new AutoResetEvent(false);

 

            CancellationTokenSource tokenSource = newCancellationTokenSource();

 

            for (int i = 0; i < 3; i++)

            {

                Task.Factory.StartNew(() =>

                {

                    while (!tokenSource.Token.IsCancellationRequested)

                    {

                        arEvent.WaitOne();

                        Console.WriteLine("Task {0} released",Task.CurrentId);

                    }

                }, tokenSource.Token);

            }

 

            Task signallingTask = Task.Factory.StartNew(() =>

            {

                while (!tokenSource.Token.IsCancellationRequested)

                {

                    tokenSource.Token.WaitHandle.WaitOne(500);

                    arEvent.Set();

                    Console.WriteLine("Event set");

                }

            }, tokenSource.Token);

 

            Console.ReadLine();

            tokenSource.Cancel();

            Console.WriteLine("Press enter to finish");

            Console.ReadLine();

5. SemaphoreSlim

对可同时访问资源或资源池的线程数加以限制的 Semaphore 的轻量级实现。其原理就是在初始化时指定同时访问资源的数量,当某个任务执行完毕之后,调用Release()方法释放资源,当某个任务需要执行时,则它可以调用Wait()方法,阻塞当前线程直到它获取到资源为止。需要注意的时,这里的资源不是指什么数据库连接,内存什么的,这里的资源可以理解成只是一个协调任务同步的标记或者信号而已。代码如下:

 var semaphore = new SemaphoreSlim(2);

            CancellationTokenSource tokenSource = newCancellationTokenSource();

            for (int i = 0; i < 10; i++)

            {

                Task.Factory.StartNew(() =>

                {

                    while (true)

                    {

                        semaphore.Wait(tokenSource.Token);

                        Console.WriteLine("Task {0} released",Task.CurrentId);

 

                    }

                }, tokenSource.Token);

            }

 

            Task signallingTask = Task.Factory.StartNew(() =>

            {

                while (!tokenSource.Token.IsCancellationRequested)

                {

                    tokenSource.Token.WaitHandle.WaitOne(500);

                    semaphore.Release(2);

                    Console.WriteLine("Semaphore released");

                }

                tokenSource.Token.ThrowIfCancellationRequested();

            }, tokenSource.Token);

 

            Console.ReadLine();

            tokenSource.Cancel();

            Console.WriteLine("Press enter to finish");

            Console.ReadLine();

可以看到,.Net 4中,提供了更多的同步方法。极大方便了,我们对任务或线程同步的处理。我们可以根据需要,选择自己合适的、熟悉的方法进行对同步处理。

 

    本文转自风车车  博客园博客,原文链接:http://www.cnblogs.com/xray2005/archive/2011/09/04/2166831.html ,如需转载请自行联系原作者



相关文章
|
6月前
|
关系型数据库 MySQL 数据库
找不到请求的 .Net Framework Data Provider。可能没有安装
做的一个项目,框架为.net framework 数据库为mysql 出现如标题错误 检查是否安装mysql、是否安装mysql connector net 笔者是因为没有安装后者mysql connector net 下载地址: [mysql connector net](https://downloads.mysql.com/archives/c-net/ "mysql connector net") 笔者安装截图如下: ![请在此添加图片描述](https://developer-private-1258344699.cos.ap-guangzhou.myqcloud.com/c
56 0
|
7月前
|
NoSQL API 调度
.NET开源的轻量化定时任务调度,支持临时的延时任务和重复循环任务(可持久化) - FreeScheduler
.NET开源的轻量化定时任务调度,支持临时的延时任务和重复循环任务(可持久化) - FreeScheduler
104 0
|
6月前
|
C# Windows
[记录]c#.net framework 4.5调用运行时库
[记录]c#.net framework 4.5调用运行时库
|
1月前
|
Windows
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
|
6月前
|
Windows
​史上最详细的Windows10系统离线安装.NET Framework 3.5的方法(附离线安装包下载)
​史上最详细的Windows10系统离线安装.NET Framework 3.5的方法(附离线安装包下载)
544 0
|
2月前
|
机器学习/深度学习 存储 编解码
多任务学习新篇章 | EMA-Net利用Cross-Task Affinity实现参数高效的高性能预测
多任务学习新篇章 | EMA-Net利用Cross-Task Affinity实现参数高效的高性能预测
42 0
|
4月前
|
C# Windows
C#安装“Windows 窗体应用(.NET Framework)”
C#安装“Windows 窗体应用(.NET Framework)”
50 0
|
7月前
|
开发框架 .NET 编译器
C#OOP之十四 .Net Framework简介
C#OOP之十四 .Net Framework简介
52 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
38 0
|
29天前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
28 0