zoukankan      html  css  js  c++  java
  • .NET 实现并行的几种方式(二)

    本随笔续接:.NET 实现并行的几种方式(一) 

    四、Task  

    3)Task.NET 4.5 中的简易方式

    在上篇随笔中,两个Demo使用的是 .NET 4.0 中的方式,代码写起来略显麻烦,这不 .NET 4.5提供了更加简洁的方式,让我们来看一下吧。

            /// <summary>
            /// Task.NET 4.5 中的简易方式
            /// </summary>
            public void Demo3()
            {
                Task.Run(() =>
                {
                    SetTip("简洁的代码");
                });
    
    
                Task.Run(() =>
                {
                    SetTip("验证 CreationOptions 属性");
                }).ContinueWith((t)=> {
                    SetTip("CreationOptions:" + t.CreationOptions.ToString());                
                });
            }
    Task.NET 4.5 中的简易方式

     

    五、TPL (Task Parallel Library)

    TPL (任务并行库)是 .NET 4.0 中的另一个重量级模块,可以极其优雅、便捷地完成并行逻辑的编码工作。

    1)Parallel.Invoke并行多个独立的Action

            /// <summary>
            /// Parallel.Invoke并行多个独立的Action
            /// </summary>
            public void Demo1()
            {
                Task.Run(() =>
                {
                    List<Action> actions = new List<Action>();
    
                    // 生成并行任务
                    for (int i = 0; i < 5; i++)
                    {
                        // 注意、这里很关键,不可直接使用i变量。 
                        // 原因在稍后的随笔中进行说明
                        int index = i;
                        actions.Add(new Action(() =>
                        {
                            SetTip(string.Format("Task{0} 开始", index));
    
                            SetTip(string.Format("Task{0} 休眠1秒", index));
                            Thread.Sleep(1000);
    
    
                            SetTip(string.Format("Task{0} 休眠5秒", index));
                            Thread.Sleep(5000);
                            
                            SetTip(string.Format("Task{0} 结束", index));
                        }));
                    }
    
                    // 执行并行任务
                    Parallel.Invoke(actions.ToArray());
    
                    // 当上述的5个任务全部执行完毕后,才会执行该代码
                    SetTip("并行任务执行完毕");
                });
            }
    Parallel.Invoke并行多个独立的Action

    2)Parallel简单的For并行

    如果 Parallel.Invoke 看做是任务并行, 则 Parallel.For 则是数据并行,可方便的完成For循环并行遍历。

            /// <summary>
            /// Parallel简单的For并行
            /// </summary>
            public void Demo2()
            {
                // 为了实时更新UI、将代码异步执行
                Task.Run(() =>
                {
                    Parallel.For(1, 100, (index) =>
                    {
                        SetTip(string.Format("Index:{0}, 开始执行Task", index));
    
                        Thread.Sleep(1000);
                        SetTip(string.Format("Index:{0}, 开始休眠Action 1秒", index));
    
                        SetTip(string.Format("Index:{0}, Task执行完毕", index));
                    });
    
                    SetTip("并行任务执行完毕");
                });
            }
    Parallel简单的For并行

    3)Parallel.For并行 并行中的 break、 return、 continue

    break : 在 Parallel.For 中使用 ParallelLoopState.Break() 方法代替。

    return: 在 Parallel.For 中使用 ParallelLoopState.Break() 方法代替。

    continue : 在 Parallel.For 中直接使用 return 即可。

            /// <summary>
            /// 中断Parallel.For并行
            /// </summary>
            public void Demo3()
            {
                // 为了实时更新UI、将代码异步执行
                Task.Run(() =>
                {
                    int breakIndex = new Random().Next(10, 50);
                    SetTip(" BreakIndex : -------------------------" + breakIndex);
    
                    Parallel.For(1, 100, (index, state) =>
                    {
                        SetTip(string.Format("Index:{0}, 开始执行Task", index));
    
                        if (breakIndex == index)
                        {
                            SetTip(string.Format("Index:{0}, ------------------ Break Task", index));
                            state.Break();
                            // Break方法执行后、
                            // 大于 当前索引的并且未被安排执行的迭代将被放弃
                            // 小于 当前索引的的迭代将继续正常执行直至迭代执行完毕
                            return;
                        }
    
                        Thread.Sleep(1000);
                        SetTip(string.Format("Index:{0}, 休眠Action 1秒", index));
    
                        SetTip(string.Format("Index:{0}, Task执行完毕", index));
                    });
    
                    SetTip("并行任务执行完毕");
                });
            }
    
    
            /// <summary>
            /// 终止Parallel.For并行
            /// </summary>
            public void Demo4()
            {
                // 为了实时更新UI、将代码异步执行
                Task.Run(() =>
                {
                    int stopIndex = new Random().Next(10, 50);
                    SetTip(" StopIndex : -------------------------" + stopIndex);
    
                    Parallel.For(1, 100, (index, state) =>
                    {
                        SetTip(string.Format("Index:{0}, 开始执行Task", index));
    
                        if (stopIndex == index)
                        {
                            SetTip(string.Format("Index:{0}, ------------------ Stop Task", index));
                            state.Stop();
                            // Stop方法执行后
                            // 整个迭代将被放弃
                            return;
                        }
    
                        Thread.Sleep(1000);
                        SetTip(string.Format("Index:{0}, 休眠Action 1秒", index));
    
                        SetTip(string.Format("Index:{0}, Task执行完毕", index));
                    });
    
                    SetTip("并行任务执行完毕");
                });
            }
    Parallel.For并行 并行中的 break、 return、 continue

    4)Parallel.For并行中的数据聚合

    在并行中,绝大多数委托都是在不同的线程中运行的,如果需要在并行中进行的数据共享、则需要考虑线程同步问题,然而线程同步会影响并行性能。

    为了解决特定情况下的数据共享,而又不会因为线程同步而影响性能,Parallel.For 提供了解决方案:

            /// <summary>
            /// Parallel.For并行中的数据聚合
            /// </summary>
            public void Demo5()
            {
                Task.Run(() =>
                {
                    // 求 1 到 10 的阶乘的 和
                    long total = 0;
                    Parallel.For<long>(1, 10,
                        () =>
                        {
                            SetTip("LocalInit");
                            return 0;
                        },
                        (index, state, local) =>
                        {
                            SetTip("Body");
                            int result = 1;
                            for (int i = 2; i < index; i++)
                            {
                                result *= i;
                            }
                            local += result;
                            return local;
                        },
                        (x) =>
                        {
                            SetTip("LocalFinally");
                            Interlocked.Add(ref total, x);
                        });
    
                    SetTip("Total : " + total);
                    SetTip("并行任务执行完毕");
                });
    
            }
    Parallel.For并行中的数据聚合

    MSDN备注:
    对于参与循环执行的每个线程调用 LocalInit 委托一次,并返回每个线程的初始本地状态。
    这些初始状态传递到每个线程上的第一个 body 调用。 然后,每个后续正文调用返回可能修改过的状态值,传递到下一个正文调用。
    最后,每个线程上的最后正文调用返回传递给 LocalFinally 委托的状态值。
    每个线程调用 localFinally 委托一次,以对每个线程的本地状态执行最终操作。
    此委托可以被多个线程同步调用;因此您必须同步对任何共享变量的访问。

    也就是说:
    1) 并行中开辟的线程数 决定了 LocalInit、LocalFinally 的调用次数
    2) 多个 迭代委托、Body 可能被同一个线程调用。
    3) 迭代委托、Body 中的 local值,并不一定是 LocalInit 的初始值,也有可能是被修改的返回值。
    4) LocalFinally 可能是被同时调用的,需要注意线程同步问题。

    5)Parallel.ForEach并行

    Parallel.ForEach算是另一种数据并行方式, 它与大家熟知的 IEnumerable<TSource> 接口结合十分紧密,是 foreach的并行版本。

            /// <summary>
            /// Parallel.ForEach并行
            /// </summary>
            public void Demo6()
            {
                Task.Run(() =>
                {
                    Parallel.ForEach<int>(Enumerable.Range(1, 10), (num) =>
                    {
                        SetTip("Task 开始");
    
    
                        SetTip("Task 休眠" + num + "");
                        Thread.Sleep(TimeSpan.FromSeconds(num));
    
    
                        SetTip("Task 结束");
                    });
    
                    SetTip("并行任务执行完毕");
                });
            }
    Parallel.ForEach并行

    6)Parallel.ForEach中的索引,中断、终止操作

    在 Parallel.ForEach 中也可以轻易的获得其遍历的索引

            /// <summary>
            /// Parallel.ForEach中的索引,中断、终止操作
            /// </summary>
            public void Demo7()
            {
                Task.Run(() =>
                {
                    Parallel.ForEach<int>(Enumerable.Range(0, 10), (num, state, index) =>
                    {
                        // num, 并行数据源中的数据项
                        // state, 
                        SetTip(" Index : " + index + "         Num: " + num);
                    });
                    SetTip("并行任务执行完毕");
                });
            }
    Parallel.ForEach中的索引,中断、终止操作

    本随笔到此、暂告一段落。

    附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

    参见更多:随笔导读:同步与异步


    (未完待续...)

  • 相关阅读:
    缓存读写策略
    支撑京东小程序的开发框架 「Taro」
    Zookeeper vs Etcd
    前端开发利器 Web Replay
    kafka 中 zookeeper 具体是做什么的?
    newSQL 到底是什么?
    zookeeper配置集群
    zookeeper配置文件说明
    ssh远程访问-提示密钥不安全
    Nodejs-log4js使用配置
  • 原文地址:https://www.cnblogs.com/08shiyan/p/5787860.html
Copyright © 2011-2022 走看看