public void testStopTaskWait() { //2020/7/17 9:47:55 before cts.cancel in thread 10 and no task // step 1 //执行cts.Cancel() // step 2 //2020/7/17 9:47:55 cancel token reg - 03 in thread 10 and no task // step 3 //2020/7/17 9:47:55 cancel token reg - 02 in thread 10 and no task // step 4 //2020/7/17 9:47:55 cancel token reg - 01 in thread 10 and no task // step 5 //Delay地方捕捉到异常 // step 6 //2020/7/17 9:47:55 after delay in thread 13 and no task // step 7 CancellationTokenSource cts = new CancellationTokenSource(); //这些注册的delegate,将在执行Cancel的时候调用到 cts.Token.Register(() => { ThreadEval_Util.TraceThreadAndTask("cancel token reg - 01"); }); // step 5 cts.Token.Register(() => { ThreadEval_Util.TraceThreadAndTask("cancel token reg - 02"); }); // step 4 cts.Token.Register(() => { ThreadEval_Util.TraceThreadAndTask("cancel token reg - 03"); }); // step 3 ThreadPool.QueueUserWorkItem(state => { ThreadEval_Util.TraceThreadAndTask("before cts.cancel"); // step 1 Thread.Sleep(10); cts.Cancel(); // step 2 }); //System.AggregateException : One or more errors occurred. (A task was canceled.) // ----> System.Threading.Tasks.TaskCanceledException : A task was canceled. Assert.Throws <AggregateException>(() => Task.Delay(100, cts.Token).Wait()); // step 6 ThreadEval_Util.TraceThreadAndTask("after delay"); // step 7 }
public void testGreetingClient() { //下面是这个过程的输出 //2020.07.17 10:11:55:536 testGreetingClient enter in thread 13 and no task //2020.07.17 10:11:55:538 callGreetingClientAsync enter in thread 13 and no task //2020.07.17 10:11:55:539 running GreetingAsync in thread 11 and task 1 //2020.07.17 10:11:55:539 running Greeting in thread 11 and task 1 //2020.07.17 10:11:55:539 testGreetingClient got Task<String> greetingResult, and next to get greetingResult.Result in thread 13 and no task //这里有个500毫秒的gap(当时Greeting方法正在blocking wait) //2020.07.17 10:11:56:050 callGreetingClientAsync done in thread 11 and no task //2020.07.17 10:11:56:050 testGreetingClient done - got greetingResult.Result:Hello, HanMeiMei in thread 13 and no task ThirdPartyAsyncSimulator greetingClient = new ThirdPartyAsyncSimulator(); //线程A - 调用者当前线程 ThreadEval_Util.TraceThreadAndTask($"{nameof(testGreetingClient)} enter"); Task <String> greetingResult = callGreetingClientAsync(); ThreadEval_Util.TraceThreadAndTask($"{nameof(testGreetingClient)} got Task<String> greetingResult, and next to get greetingResult.Result"); //线程A - 前面都很快,而执行这个获取task结果卡住了,因为结果还没有出来 String greetingResultStr = greetingResult.Result; //线程A - 这里线程没变 ThreadEval_Util.TraceThreadAndTask($"{nameof(testGreetingClient)} done - got greetingResult.Result:{greetingResultStr}"); }
//尝试过去掉async/await,结果没啥两样。加上他们是为了让代码看起来更一致 private async Task <String> forgedTaskFromResultAsync() { ThreadEval_Util.TraceThreadAndTask("forgedTaskFromResultAsync - before await"); var result = await Task.FromResult(longRunTask()); ThreadEval_Util.TraceThreadAndTask("forgedTaskFromResultAsync - after await"); return(result); }
public Task <String> GreetingAsync(String name) { Task <String> result = Task.Run <String>(() => { ThreadEval_Util.TraceThreadAndTask($"running {nameof(GreetingAsync)}"); return(Greeting(name)); }); return(result); }
/** * * 这里给出了1个使用httpclient async方法的例子(其实也没看出来节省时间!因为最外部调用方法还是要等待) * 注释掉了[Test]是因为其有外部依赖(baidu.com). 这个调用过程用上面的Greeting完全是一致的 * */ //[Test] public void testHttpClient() { //线程A ThreadEval_Util.TraceThreadAndTask("{nameof(testHttpClient)} enter"); Task <int> lengthTask = GetPageLengthAsync("http://www.baidu.com"); //在async方法运行的时候,当前task已经返回到这里了,但是如果这里获取结果的话,则继续等待直到async方法执行完毕 //也就是说,从这儿的角度,其并没有加快什么 //除非当前这里的业务逻辑不在乎task执行结果(我是说这一行不在乎执行结果,执行结果可以在GetPageLengthAsync里的线程B中做某种处理)································································································································································································································································································································································································································································································································································································································································································································································································································ Console.WriteLine(lengthTask.Result); //线程A - 这里线程没变 ThreadEval_Util.TraceThreadAndTask("{nameof(testHttpClient)} done"); }
public async Task testTaskAsyncDelay() { //2020.07.17 10:09:59:294 before delay in thread 13 and no task - step 1 - 调用者线程 //调用者碰到await直接返回,继续执行其他工作(这里的调用者实际上为NUnit测试框架) - step 2 //2020.07.17 10:09:59:394 after delay in thread 9 and no task - step 3 - 100毫秒之后,新线程继续执行 Int32 delayInterval = 100; //线程A ThreadEval_Util.TraceThreadAndTask("before delay"); //step 1 //线程A执行到await之后,立马返回 await Task.Delay(delayInterval); //step 2 //过delayInterval毫秒之后,线程B继续执行下面逻辑 //线程B ThreadEval_Util.TraceThreadAndTask("after delay"); //step 3 }
public void testTaskFromResult() { //全部都是在同步调用,只不过可以融入到async调用的moshi //2020.07.17 22:11:27:820 forgedTaskFromResultAsync - before await in thread 13 and no task //2020.07.17 22:11:27:822 longRunTask in thread 13 and no task //2020.07.17 22:11:30:825 forgedTaskFromResultAsync - after await in thread 13 and no task //2020.07.17 22:11:30:825 task got in thread 13 and no task //2020.07.17 22:11:30:825 task result got in thread 13 and no task var task = forgedTaskFromResultAsync(); ThreadEval_Util.TraceThreadAndTask("task got"); var result = task.Result; ThreadEval_Util.TraceThreadAndTask("task result got"); }
static async Task <int> GetPageLengthAsync(string url) { using (HttpClient client = new HttpClient()) { //线程A ThreadEval_Util.TraceThreadAndTask("{nameof(GetPageLengthAsync)} enter"); Task <String> fetchTextTask = client.GetStringAsync(url); //代码执行到这里方法就返回了。这个耗时的client.GetStringAsync(url)将在 //<=====这个await的magic之处在于前后的线程变了 int length = (await fetchTextTask).Length; //线程B 《=== 不再是线程A了!!!! ThreadEval_Util.TraceThreadAndTask("{nameof(GetPageLengthAsync)} done"); //注意,函数返回类型为Task<int>, 但是我们这里要返回int return(length); } }
public async Task <String> callGreetingClientAsync() { ThirdPartyAsyncSimulator greetingClient = new ThirdPartyAsyncSimulator(); //线程A - 就是调用者所在的线程 ThreadEval_Util.TraceThreadAndTask($"{nameof(callGreetingClientAsync)} enter"); Task <String> greetingResult = greetingClient.GreetingAsync("HanMeiMei"); //一旦开始await,这个方法就返回了。调用方法立马得到了Task<String>的结果 //String greetingResultStr = await greetingResult; //这里使用await greetingResult与 await greetingResult.ConfigureAwait(false)是一样的 //note:但是如果是UI程序,或者asp.net则不一样 //使用ConfigureAwait(false)的目的是让醒来后的线程可以是随意一个线程池中的线程 //见本类开头更多总结 String greetingResultStr = await greetingResult.ConfigureAwait(false); //线程B - await之后,task执行之后,处理执行结果是其他线程(不再保证是进入是的线程A) ThreadEval_Util.TraceThreadAndTask($"{nameof(callGreetingClientAsync)} done"); return(greetingResultStr); }
public void testResetEvent() { //ThreadEval_QueueUserWorkItem::testBasicTaskExecution中,使用了ManualResetEvent //注意:如果在Wait之前就发送了信号by Set(),也没关系 //下面就是证明这个的, WaitOne执行的时候,Set已经执行完了。WaitOne还是顺利的继续执行 //false:not signaled, 就是需要等待一个新信号才能执行的意思,就是执行WaitOne的时候会block的意思 //true:signaled, 直接WaitOne就能返回了 ManualResetEvent mre = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(state => { Thread.Sleep(5); ThreadEval_Util.TraceThreadAndTask("002 before set"); mre.Set(); }); Thread.Sleep(1000); ThreadEval_Util.TraceThreadAndTask("002 before wait"); for (int i = 0; i < 5; i++) { //因为没有执行Reset,其一直是signaled状态,所以WaitOne一直可以返回 mre.WaitOne(); //很可惜没有等待时间超时设置 } ThreadEval_Util.TraceThreadAndTask("003 got signal"); }
private String longRunTask() { ThreadEval_Util.TraceThreadAndTask("longRunTask"); Task.Delay(3000).Wait(); return("abc"); }
public void testParallelFor_3delegates() { var nums = Enumerable.Range(1, 99); //ForEach<TSource, TLocal> //- TSource: 顾名思义,就是输入数据的类型 //- TLocal是第一个LocalInit方法返回值。该值在整个上下文中使用 // - 之后,其被传入body方法中(最后的那个参数) // - 特别注意:body中调用的方法也要返回该类型 // - 最后LocalFinally方法的输入参数类型就是它,值将一路传递下来 // //- 特别注意: 该方法还有更多参数(譬如增加option),参考文档 // //, , //如何Cancel,看这连接https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-cancel-a-parallel-for-or-foreach-loop ParallelOptions options = new ParallelOptions(); CancellationTokenSource cts = new CancellationTokenSource(); options.CancellationToken = cts.Token; //cts.Cancel();外部线程执行这个就cancel了 ParallelLoopResult result = Parallel.ForEach <Int32, Int32>( //IEnumerable<TSource> source nums, options, //特别注意:每个线程运行一次 //Func<TLocal> localInit - 输入值:empty,返回值TLocal的方法 () => { ThreadEval_Util.TraceThreadAndTask("localInit"); return(0); }, //Func<TSource, ParallelLoopState, long, TLocal, TLocal> body - 输入值:TSource, ParallelLoopState, long, TLocal,返回值:TLocal //最后一个输入参数:taskLocalTotal 是从localInit中传过来的,可以在body方法中更新它 (i, loopState, index, taskLocalTotal) => { //注意:stop/break都不会从当前方法返回,当前方法还会继续执行完 //但是新的iteration大概率不会继续开始,这是有Parallel框架来判断的 //Stop ThreadEval_Util.TraceThreadAndTask($"004-body:{i}-{index} before stop"); //应该用if(options.CancellationToken.IsCancellationRequested),外部调用 //可以使用token.ThrowIfCancellationRequested(), 不过Parallel外部要捕捉异常 //参考 https://stackoverflow.com/questions/8818203/what-is-difference-between-loopstate-break-loopstate-stop-and-cancellationt //这里为了测试,直接比较i>10 if (i > 10) { loopState.Stop(); //loopState.Break(); ThreadEval_Util.TraceThreadAndTask($"003-body:{i}-{index} stop here"); } ThreadEval_Util.TraceThreadAndTask($"003-body:{i}-{index} after stop"); Thread.Sleep(100); //loopState.Stop(); return(DoWork(i)); }, //特别注意:每个线程运行一次 //Action<TLocal> localFinally - 输入值:TLocal(即talksLocalTotal), 返回值:empty //talksLocalTotal是从body中传过来的 talksLocalTotal => { ThreadEval_Util.TraceThreadAndTask("localFinally"); } ); Console.WriteLine($"result.LowestBreakIteration:{result.LowestBreakIteration.ToString()}"); }
public void testParallelForOfAsync() { //2020.07.17 12:12:48:143 S 3 in thread 14 and task 4 //2020.07.17 12:12:48:143 S 1 in thread 11 and task 2 //2020.07.17 12:12:48:143 S 4 in thread 15 and task 5 //2020.07.17 12:12:48:143 S 2 in thread 6 and task 3 //2020.07.17 12:12:48:143 S 0 in thread 13 and task 1 //2020.07.17 12:12:48:144 S 6 in thread 18 and task 7 //2020.07.17 12:12:48:143 S 5 in thread 16 and task 6 //2020.07.17 12:12:48:144 S 7 in thread 17 and task 8 //2020.07.17 12:12:49:029 E 7 in thread 17 and task 8 //2020.07.17 12:12:49:029 E 5 in thread 16 and task 6 //2020.07.17 12:12:49:029 E 6 in thread 18 and task 7 //2020.07.17 12:12:49:029 E 0 in thread 13 and task 1 //2020.07.17 12:12:49:029 S 8 in thread 19 and task 9 //2020.07.17 12:12:49:029 E 4 in thread 15 and task 5 //2020.07.17 12:12:49:029 S 9 in thread 13 and task 1 //2020.07.17 12:12:49:029 E 1 in thread 11 and task 2 //2020.07.17 12:12:49:029 E 3 in thread 14 and task 4 //2020.07.17 12:12:49:029 E 2 in thread 6 and task 3 //2020.07.17 12:12:49:234 E 9 in thread 13 and task 1 //2020.07.17 12:12:49:234 E 8 in thread 19 and task 9 //2020.07.17 12:12:49:234 Completed - WITHOUT async/with - True in thread 13 and no task //这是没有async/await的情况,用于对比 //可以看到S/E所使用的线程是一致是一样的;还可以看到这个Delay的时间不是很准确,我要delay200毫秒,而这里显示达到了900毫秒左右。 ParallelLoopResult result1 = Parallel.For(0, 10, i => { ThreadEval_Util.TraceThreadAndTask($"S {i}"); Task.Delay(200).Wait(); ThreadEval_Util.TraceThreadAndTask($"E {i}"); }); ThreadEval_Util.TraceThreadAndTask($"Completed - WITHOUT async/with - {result1.IsCompleted}"); //2020.07.17 12:12:49:236 S 0 in thread 13 and task 10 //2020.07.17 12:12:49:236 S 6 in thread 14 and task 11 //2020.07.17 12:12:49:236 S 7 in thread 11 and task 12 //2020.07.17 12:12:49:236 S 2 in thread 19 and task 13 //2020.07.17 12:12:49:236 S 8 in thread 18 and task 14 //2020.07.17 12:12:49:236 S 1 in thread 17 and task 15 //2020.07.17 12:12:49:236 S 3 in thread 16 and task 16 //2020.07.17 12:12:49:236 S 4 in thread 15 and task 17 //2020.07.17 12:12:49:236 S 5 in thread 6 and task 18 //2020.07.17 12:12:49:238 S 9 in thread 13 and task 10 //2020.07.17 12:12:49:239 Completed - WITH async/with - True in thread 13 and no task //2020.07.17 12:12:49:429 E 9 in thread 6 and no task //2020.07.17 12:12:49:429 E 3 in thread 11 and no task //2020.07.17 12:12:49:429 E 8 in thread 16 and no task //2020.07.17 12:12:49:429 E 1 in thread 18 and no task //2020.07.17 12:12:49:429 E 4 in thread 17 and no task //2020.07.17 12:12:49:429 E 7 in thread 14 and no task //2020.07.17 12:12:49:429 E 5 in thread 19 and no task //2020.07.17 12:12:49:429 E 2 in thread 15 and no task //2020.07.17 12:12:49:429 E 0 in thread 11 and no task //2020.07.17 12:12:49:429 E 6 in thread 19 and no task //1. 这里可以看到,S/E所对应的线程是不同的 //2. Parallel.For方法直接返回了(没有等Delay执行完成 - Completed直接打印了) // 过了一会儿,await完成后其他线程把await后的结果执行一遍 ParallelLoopResult result2 = Parallel.For(0, 10, async i => { ThreadEval_Util.TraceThreadAndTask($"S {i}"); await Task.Delay(200); ThreadEval_Util.TraceThreadAndTask($"E {i}"); }); ThreadEval_Util.TraceThreadAndTask($"Completed - WITH async/with - {result1.IsCompleted}"); //下面这个Sleep是有必要的,因为使用了async之后,Parallel调用直接返回(碰到await就返回了),不等待Delay。 //如果不加这个Sleep,测试case就直接完成了,则后续的ThreadEval_Util.TraceThreadAndTask($"E {i}");无法打印了。 Thread.Sleep(250); }
private String Greeting(String name) { ThreadEval_Util.TraceThreadAndTask($"running {nameof(Greeting)}"); Task.Delay(500).Wait(); return($"Hello, {name}"); }