Task 有静态方法WaitAll和WaitAny,主要用于等待其他Task完成后做一些事情,先看看其实现部分吧:

public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
{
//Waits for all of the provided Task objects to complete execution.
public static void WaitAll(params Task[] tasks)
{
WaitAll(tasks, Timeout.Infinite);
} //Waits for any of the provided Task objects to complete execution.Return The index of the completed task in the tasks array argument.
public static int WaitAny(params Task[] tasks)
{
int waitResult = WaitAny(tasks, Timeout.Infinite);
Contract.Assert(tasks.Length == || waitResult != -, "expected wait to succeed");
return waitResult;
} //true if all of the Task instances completed execution within the allotted time; otherwise, false.
public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
if (tasks == null)
{
throw new ArgumentNullException("tasks");
}
if (millisecondsTimeout < -)
{
throw new ArgumentOutOfRangeException("millisecondsTimeout");
}
Contract.EndContractBlock();
cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations List<Exception> exceptions = null;
List<Task> waitedOnTaskList = null;
List<Task> notificationTasks = null; // If any of the waited-upon tasks end as Faulted or Canceled, set these to true.
bool exceptionSeen = false, cancellationSeen = false;
bool returnValue = true; // Collects incomplete tasks in "waitedOnTaskList"
for (int i = tasks.Length - ; i >= ; i--)
{
Task task = tasks[i];
if (task == null)
{
throw new ArgumentException(Environment.GetResourceString("Task_WaitMulti_NullTask"), "tasks");
} bool taskIsCompleted = task.IsCompleted;
if (!taskIsCompleted)
{
// try inlining the task only if we have an infinite timeout and an empty cancellation token
if (millisecondsTimeout != Timeout.Infinite || cancellationToken.CanBeCanceled)
{
AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
}
else
{
// We are eligible for inlining. If it doesn't work, we'll do a full wait.
taskIsCompleted = task.WrappedTryRunInline() && task.IsCompleted; // A successful TryRunInline doesn't guarantee completion
if (!taskIsCompleted) AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
}
}
if (taskIsCompleted)
{
if (task.IsFaulted) exceptionSeen = true;
else if (task.IsCanceled) cancellationSeen = true;
if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
}
} if (waitedOnTaskList != null)
{
// Block waiting for the tasks to complete.
returnValue = WaitAllBlockingCore(waitedOnTaskList, millisecondsTimeout, cancellationToken); // If the wait didn't time out, ensure exceptions are propagated, and if a debugger is
// attached and one of these tasks requires it, that we notify the debugger of a wait completion.
if (returnValue)
{
foreach (var task in waitedOnTaskList)
{
if (task.IsFaulted) exceptionSeen = true;
else if (task.IsCanceled) cancellationSeen = true;
if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: );
}
} GC.KeepAlive(tasks);
} if (returnValue && notificationTasks != null)
{
foreach (var task in notificationTasks)
{
if (task.NotifyDebuggerOfWaitCompletionIfNecessary()) break;
}
} // If one or more threw exceptions, aggregate and throw them.
if (returnValue && (exceptionSeen || cancellationSeen))
{
if (!exceptionSeen) cancellationToken.ThrowIfCancellationRequested(); // Now gather up and throw all of the exceptions.
foreach (var task in tasks) AddExceptionsForCompletedTask(ref exceptions, task);
Contract.Assert(exceptions != null, "Should have seen at least one exception");
throw new AggregateException(exceptions);
} return returnValue;
} public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
if (tasks == null)
{
throw new ArgumentNullException("tasks");
}
if (millisecondsTimeout < -)
{
throw new ArgumentOutOfRangeException("millisecondsTimeout");
}
Contract.EndContractBlock();
cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations int signaledTaskIndex = -;
for (int taskIndex = ; taskIndex < tasks.Length; taskIndex++)
{
Task task = tasks[taskIndex];
if (task == null)
{
throw new ArgumentException(Environment.GetResourceString("Task_WaitMulti_NullTask"), "tasks");
}
if (signaledTaskIndex == - && task.IsCompleted)
{
signaledTaskIndex = taskIndex;
}
} if (signaledTaskIndex == - && tasks.Length != )
{
Task<Task> firstCompleted = TaskFactory.CommonCWAnyLogic(tasks);
bool waitCompleted = firstCompleted.Wait(millisecondsTimeout, cancellationToken);
if (waitCompleted)
{
Contract.Assert(firstCompleted.Status == TaskStatus.RanToCompletion);
signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result);
Contract.Assert(signaledTaskIndex >= );
}
}
GC.KeepAlive(tasks);
return signaledTaskIndex;
} //Performs a blocking WaitAll on the vetted list of tasks.true if all of the tasks completed; otherwise, false.
private static bool WaitAllBlockingCore(List<Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
Contract.Assert(tasks != null, "Expected a non-null list of tasks");
Contract.Assert(tasks.Count > , "Expected at least one task");
bool waitCompleted = false;
var mres = new SetOnCountdownMres(tasks.Count);
try
{
foreach (var task in tasks)
{
task.AddCompletionAction(mres, addBeforeOthers: true);
}
waitCompleted = mres.Wait(millisecondsTimeout, cancellationToken);
}
finally
{
if (!waitCompleted)
{
foreach (var task in tasks)
{
if (!task.IsCompleted) task.RemoveContinuation(mres);
}
}
}
return waitCompleted;
} private sealed class SetOnCountdownMres : ManualResetEventSlim, ITaskCompletionAction
{
private int _count; internal SetOnCountdownMres(int count)
{
Contract.Assert(count > , "Expected count > 0");
_count = count;
} public void Invoke(Task completingTask)
{
if (Interlocked.Decrement(ref _count) == 0) Set();
Contract.Assert(_count >= , "Count should never go below 0");
}
}
}

我们首先看看WaitAll的方法,检查Task数组中每个Task实例,检查Task是否已经完成,如果没有完成就把Task添加到waitedOnTaskList集合中,如果waitedOnTaskList集合有元素那么,我们就调用WaitAllBlockingCore来实现真正的等待,当代完毕后我们需要检查notificationTasks集合是否有元素,如果有则依次调用Task的NotifyDebuggerOfWaitCompletionIfNecessary方法。WaitAllBlockingCore实现阻塞是依靠SetOnCountdownMres实例的【和CountdownEvent思路一样,每次调用Invoke的时候,就把计数器_count减1,当_count==0时就调用Set方法】,在WaitAllBlockingCore方法退出前,需要检查Task是否都完成,如果有没有完成的需要移除相应task的SetOnCountdownMres实例【if (!task.IsCompleted) task.RemoveContinuation(mres);】,SetOnCountdownMres的Invoke方法是在Task的FinishContinuations方法调用的【 ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction; singleTaskCompletionAction.Invoke(this);注意FinishContinuations方法是在FinishStageThree中调用】注意里面的GC.KeepAlive(tasks)。

现在我们来看看WaitAny方法的实现,首先我们需要循环Task[],检查里面是否有Task已经完成,如果有则直接返回,否者我们调用Task<Task> firstCompleted = TaskFactory.CommonCWAnyLogic(tasks);返回一个Task,然后调用该Task的Wait方法【bool waitCompleted = firstCompleted.Wait(millisecondsTimeout, cancellationToken);】,让我们来看看CommonCWAnyLogic的实现:

public class TaskFactory
{
internal static Task<Task> CommonCWAnyLogic(IList<Task> tasks)
{
Contract.Requires(tasks != null);
var promise = new CompleteOnInvokePromise(tasks);
bool checkArgsOnly = false;
int numTasks = tasks.Count;
for(int i=; i<numTasks; i++)
{
var task = tasks[i];
if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks"); if (checkArgsOnly) continue; // If the promise has already completed, don't bother with checking any more tasks.
if (promise.IsCompleted)
{
checkArgsOnly = true;
}
// If a task has already completed, complete the promise.
else if (task.IsCompleted)
{
promise.Invoke(task);
checkArgsOnly = true;
}
// Otherwise, add the completion action and keep going.
else task.AddCompletionAction(promise);
}
return promise;
}
internal sealed class CompleteOnInvokePromise : Task<Task>, ITaskCompletionAction
{
private IList<Task> _tasks; // must track this for cleanup
private int m_firstTaskAlreadyCompleted; public CompleteOnInvokePromise(IList<Task> tasks) : base()
{
Contract.Requires(tasks != null, "Expected non-null collection of tasks");
_tasks = tasks; if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "TaskFactory.ContinueWhenAny", ); if (Task.s_asyncDebuggingEnabled)
{
AddToActiveTasks(this);
}
} public void Invoke(Task completingTask)
{
if (Interlocked.CompareExchange(ref m_firstTaskAlreadyCompleted, , ) == )
{
if (AsyncCausalityTracer.LoggingOn)
{
AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Choice);
AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
} if (Task.s_asyncDebuggingEnabled)
{
RemoveFromActiveTasks(this.Id);
} bool success = TrySetResult(completingTask);
Contract.Assert(success, "Only one task should have gotten to this point, and thus this must be successful."); var tasks = _tasks;
int numTasks = tasks.Count;
for (int i = ; i < numTasks; i++)
{
var task = tasks[i];
if (task != null && // if an element was erroneously nulled out concurrently, just skip it; worst case is we don't remove a continuation
!task.IsCompleted) task.RemoveContinuation(this);
}
_tasks = null; }
}
}
}

CommonCWAnyLogic首先实例化CompleteOnInvokePromise【var promise = new CompleteOnInvokePromise(tasks)】,检查promise 是否完成,检查每个Task是否完成,否者就把promise作为Task的Continue Task【这里可以理解为每个Task都有一个相同Continue Task】,而CompleteOnInvokePromise自己的wait是在WaitAny中的firstCompleted.Wait(millisecondsTimeout, cancellationToken)方法,当其中其中一个Task完成后,在Task的FinishContinuations方法调用的CompleteOnInvokePromise的Invoke【一旦触发后就需要移调其他task上的CompleteOnInvokePromise,如这里的task.RemoveContinuation(this)】。在CompleteOnInvokePromise的Invoke方法我们调用TrySetResult(completingTask)方法,期实现如下:

public class Task<TResult> : Task
{
internal bool TrySetResult(TResult result)
{
if (IsCompleted) return false;
Contract.Assert(m_action == null, "Task<T>.TrySetResult(): non-null m_action");
if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED,
TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
{
m_result = result;
Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION); var cp = m_contingentProperties;
if (cp != null) cp.SetCompleted();
FinishStageThree();
return true;
}
return false;
}
}

这里的TrySetResult方法里面调用FinishStageThree方法,以保证Task后面的Continue Task的执行。

C# Task WaitAll和WaitAny的更多相关文章

  1. Task.WaitAll代替WaitHandle.WaitAll

    Task.Waitall阻塞了当前线程直到全完.whenall开启个新监控线程去判读括号里的所有线程执行情况并立即返回,等都完成了就退出监控线程并返回监控数据. task.Result会等待异步方法返 ...

  2. Task WaitAll的用法

    var tasklst = new List<Task>(); ; i < urls.Count; i++) { tasklst.Add(Task.Factory.StartNew& ...

  3. c# Task waitAll,WhenAll

    wait 阻塞的 when是异步的非阻塞的. Task[] tlist = new Task[] { Task.Run(() => { Thread.Sleep(3000); }), Task. ...

  4. C#当中使用async和await

    最近在写程序的时候,经常遇到大量需要异步访问的情况,但是对于async和await到底怎么写,还不是非常明确.于是参考<C#图解教程>了异步编程一节. 1.普通的程序怎么写? class ...

  5. c#task list waitall task waitany

    上边的主线程会被卡住: 7个同时开发,谁先完成之后, 需要完成某个工作,可以用waitany 2: 不卡界面的做法 加上以上命令就不卡,是子线程做的事 //Task task = taskFactor ...

  6. .Net多线程编程—任务Task

    1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 object AsyncState 表示在 ...

  7. c# .Net并行和多线程编程之Task学习记录!

    任务Task和线程Thread的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线 ...

  8. 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  9. 细说.NET中的多线程 (三 使用Task)

    上一节我们介绍了线程池相关的概念以及用法.我们可以发现ThreadPool. QueueUserWorkItem是一种起了线程之后就不管了的做法.但是实际应用过程,我们往往会有更多的需求,比如如果更简 ...

随机推荐

  1. Codeforces 758F Geometrical Progression

    Geometrical Progression n == 1的时候答案为区间长度, n == 2的时候每两个数字都可能成为答案, 我们只需要考虑 n == 3的情况, 我们可以枚举公差, 其分子分母都 ...

  2. 51Nod1231 记分牌 动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1231.html 题目传送门 - 51Nod1231 题意 题解 显然是一个竞赛图相关的题. 我们首先 ...

  3. 016 pickle

    英文也是泡菜的意思. 学完了,还是感觉这个模块是蛮不错的,对多数据保存到文件中,然后在使用的时候,再读取出来,让程序闲的更加优雅,简洁. 一:介绍 1.为什么使用 在开篇已经介绍了,但是我这里粘贴一下 ...

  4. day33 网络编程之线程,并发以及selectors模块io多路复用

    io多路复用 selectors模块 概要: 并发编程需要掌握的知识点: 开启进程/线程 生产者消费者模型!!! GIL全局解释器锁(进程与线程的区别和应用场景) 进程池线程池 IO模型(理论) 1 ...

  5. 工作记录之 [ python请求url ] v s [ java请求url ]

    背景: 模拟浏览器访问web,发送https请求url,为了实验需求需要获取ipv4数据包 由于不做后续的内容整理(有内部平台分析),故只要写几行代码请求发送https请求url列表中的url即可 开 ...

  6. HDU 5178 pairs【二分】||【尺取】

    <题目链接> 题目大意: 给定一个整数序列,求出绝对值小于等于k的有序对个数. 解题分析: $O(nlong(n))$的二分很好写,这里就不解释了.本题尺取$O(n)$也能做,并且效率很不 ...

  7. a标签点击不跳转的几种方法

    a标签点击不跳转的几种方法 1.onclick事件中返回false <a href="http://www.baidu.com" onclick="return f ...

  8. Oracle Loop

    1. LOOP - END LOOP - EXIT declare v_rlt ):; begin v_rlt:; loop dbms_output.put_line('loop'||v_rlt); ...

  9. SpringMVC(十六) 处理模型数据之SessionAttributes

    @SessionAttributes原理 默认情况下Spring MVC将模型中的数据存储到request域中.当一个请求结束后,数据就失效了.如果要跨页面使用.那么需要使用到session.而@Se ...

  10. BZOJ.4818.[SDOI2017]序列计数(DP 快速幂)

    BZOJ 洛谷 竟然水过了一道SDOI!(虽然就是很水...) 首先暴力DP,\(f[i][j][0/1]\)表示当前是第\(i\)个数,所有数的和模\(P\)为\(j\),有没有出现过质数的方案数. ...