zoukankan      html  css  js  c++  java
  • 并发系列64章(并行编程二)第六章

    前言

    续前一章。

    1.并行调用

    2.动态并行

    3.并行linq

    并行调用

    指的是调用一批方法,并且这些方法是(大部分)相互独立的。

    static void ProcessArray(double[] array)
    {
    	Parallel.Invoke(
    		() => { ProcessPartialArray(array, 0, array.Length / 2); },
    		() => { ProcessPartialArray(array, array.Length / 2, array.Length); }
    		);
    }
    
    static void ProcessPartialArray(double[] array, int begin,int end)
    {
    	// 开始计算
    }
    

    当然我们不知道到底有多少方法需要并行的时候,可以这样:

    static void DoActionTimes(CancellationToken token, params Action[] action)
    {
    	Parallel.Invoke(new ParallelOptions { CancellationToken=token} ,action);
    }
    

    之所以填写一个token进来,是希望有一个可以取消的状态。

    动态并行

    上面的并行调用中,解决了一个问题,就是在确定要并行的数量的时候,我们通过传入一个action数组,来实现。

    但是呢,有时候我们不确定到底我们多少并行数量。上文中,我们action是一个数组已经确定了数量了,因为数组可以简单的遍历,通过下标就可以得到每一个并行委托。

    有些却不能,如链表,树,图等,复制一些的数据,需要遍历计算的时候,就是在运行的时候才知道数量级是多少,所以又叫动态并行。

    public class Node
    {
    	public Node left;
    	public Node right;
    }
    
    static void Travrese(Node current)
    {
    	// 对current 做一些操作
    	if (current.left!=null)
    	{
    		Task.Factory.StartNew(() => Travrese(current.left),
    			CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
    	}
    
    	if (current.right != null)
    	{
    		Task.Factory.StartNew(() => Travrese(current.right),
    			CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
    	}
    }
    
    static void ProcessTree(double[] array)
    {
    	Node root = new Node();
    	var task= Task.Factory.StartNew(() => Travrese(root),
    			CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
    	task.Wait();
    }
    

    关键部分,在TaskCreationOptions.AttachedToParent;

    //
    // 摘要:
    //     指定将任务附加到任务层次结构中的某个父级。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 System.Threading.Tasks.TaskContinuationOptions.AttachedToParent
    //     选项以便将父任务和子任务同步。 请注意,如果使用 System.Threading.Tasks.TaskCreationOptions.DenyChildAttach
    //     选项配置父任务,则子任务中的 System.Threading.Tasks.TaskCreationOptions.AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。
    //     有关详细信息,请参阅附加和分离的子任务。
    

    这种情况是父任务和子任务。

    在此解释一下,task.wait() 因为父任务和子任务同步了,所以说这个await是等待了所以任务完成后,这里的并行是子任务是并行的,当然不能完全这么说,父父子子,子又是父,大概就是这个意思哈。

    如果没有子任务等待你可以这样:

    var task= Task.Factory.StartNew(() => { },
    		CancellationToken.None, TaskCreationOptions. None, TaskScheduler.Default);
    
    task.ContinueWith(t => Trace.WriteLine("Task is dome"),
    	CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default
    	);
    

    上面的code不是并行的范畴,而是异步编程的范畴了。

    当然,上面的动态并行也可以这样写。

    if (current.left!=null)
    {
       var leftTask=Task.Factory.StartNew(() => Travrese(current.left),
    		CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
    }
    
    if (current.right != null)
    {
       var rightTask=Task.Factory.StartNew(() => Travrese(current.right),
    		CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
    }
    Task.WaitAll(leftTask, rightTask);
    

    当然了,上面肯定会报错,因为leftTask, rightTask拿不到。由此我们可见通过建立父子任务,这样在做一些判断的时候 current.right != null

    来决定是否创建task的时候,waitall显得笨重,因为它必须确定task的数量。

    并行linq

    这个就是希望我们可以使用linq了,linq不仅仅是为了使我们的代码方便,而是一种编程思想,还很多好处,这里就不列举了,百度很多。

    static IEnumerable<int> MultiplyBy2(IEnumerable<int> values)
    {
    	return values.AsParallel().Select(item => item * 2);
    }
    

    这样是没有顺序的。

    return values.AsParallel().AsOrdered().Select(item => item * 2);
    

    这样是有顺序的,试想一下,如果让我们去实现并行后IEnumerable 按照原来的顺序,是不是非常麻烦。

    总之,能使用linq就用linq吧。

    下一章

    整理了基础的数据流。

  • 相关阅读:
    EnterpriseLibrary
    如何只保证窗口只打开一次[即只运行一个进程]
    设计模式之工厂方法模式
    设计模式之代理类
    asp.net mvc应用架构的思考--Unity的应用及三层代码
    为什么我说不要用TransactionScope
    Redis入门学习
    实战分层架构
    asp.net mvc 4多级area实现技巧
    jsonp其实很简单【ajax跨域请求】
  • 原文地址:https://www.cnblogs.com/aoximin/p/12710564.html
Copyright © 2011-2022 走看看