环境: Windows Server 2012,.net 4.5,visual studio 2013,

注意:不是UI应用程序(因此与着名的async / await / synchronizationcontext问题无关)(参考:http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Async-library-methods-should-consider-using-Task-ConfigureAwait-false-)

编辑

其实我的不好 – 这是TYPO导致了交易.我粘贴了下面的样本片段(伪),导致死锁.基本上,我没有基于’childtasks’进行组合,而是在’外部任务’上做了:(.看起来我不应该在看电视时写’异步’代码:).

我已经离开原始代码片段,因为它确实回答了我的第一个问题(与我之前的两个代码片段中的async / await和unwrap的区别).死锁让我分心看到实际的问题:).谢谢大家的评论.

static void Main(string[] args)
{
    Task t = IndefinitelyBlockingTask();
    t.Wait();            
}        
static Task IndefinitelyBlockingTask()
{
    List<Task> tasks = new List<Task>();
    Task task = FooAsync();
    tasks.Add(task);
    Task<Task> continuationTask = task.ContinueWith(t =>
    {
        Task.Delay(10000);
        List<Task> childtasks = new List<Task>();
        ////get child tasks
        //Now INSTEAD OF ADDING CHILD TASKS,i added outer method TASKS. Typo :(:)!
        Task wa = Task.WhenAll(tasks/*TYPO*/);
        return wa;
    },TaskContinuationoptions.OnlyOnRanToCompletion);
    tasks.Add(continuationTask);
    Task unwrappedTask = continuationTask.Unwrap();
    tasks.Add(unwrappedTask);
    Task whenall = Task.WhenAll(tasks.ToArray());
    return whenall;
}

代码片段在“解包”延续任务被无限期等待时被添加到我已经粘贴在伪(我的实际应用中的模式/成语块 – 而不是样本)下面的任务的聚合/链中,代码片段(#1)无限期地等待’unwrapped’任务已添加到列表中. VS Debugger的“Debugger windows threads”窗口显示该线程只是在ManualResetEventSlim.Wait上阻塞.

代码片段与async / await一起使用,并删除未解包的任务然后我删除(在调试时随机),这个unwrap语句并在lambda中使用async / await(请参见下文).令人惊讶的是它有效.但我不确定为什么:(?.

问题

>不使用unwrap和async / await在下面的代码片段中提供相同的用途吗?我最初只是首选片段#1,因为我只是想避免生成过多的代码,因为调试器不是那么友好(特别是在异常通过链式任务传播的错误情况下 – 异常中的callstack显示movenext而不是我的实际代码) .如果是,那么这是TPL中的错误吗?
>我错过了什么?如果它们相同,哪种方法更受欢迎?

关于Debugger Tasks窗口’Debugger Tasks’窗口的注意事项没有显示任何细节(注意它在我的环境中工作不正确(至少我的理解),因为它从不显示未安排的任务并且显示活动任务)

无限期等待ManualResetEventSlim.Wait的代码片段

static Task IndefinitelyBlockingTask()
{
    List<Task> tasks = new List<Task>();
    Task task = FooAsync();
    tasks.Add(task);
    Task<Task> continuationTask = task.ContinueWith(t =>
    {
        List<Task> childTasks = new List<Task>();
        for (int i = 1; i <= 5; i++)
        {
            var ct = FooAsync();
            childTasks.Add(ct);
        }
        Task wa = Task.WhenAll(childTasks.ToArray());
        return wa;
    },TaskContinuationoptions.OnlyOnRanToCompletion);
    tasks.Add(continuationTask);
    Task unwrappedTask = continuationTask.Unwrap();
    //commenting below code and using async/await in lambda works (please see below code snippet)
    tasks.Add(unwrappedTask);
    Task whenall = Task.WhenAll(tasks.ToArray());
    return whenall;
}

代码片段在lambda中使用async / await而不是unwrap

static Task TaskWhichWorks()
{
    List<Task> tasks = new List<Task>();
    Task task = FooAsync();
    tasks.Add(task);
    Task<Task> continuationTask = task.ContinueWith(async t =>
    {
        List<Task> childTasks = new List<Task>();
        for (int i = 1; i <= 5; i++)
        {
            var ct = FooAsync();
            childTasks.Add(ct);
        }
        Task wa = Task.WhenAll(childTasks.ToArray());
        await wa.ConfigureAwait(continueOnCapturedContext: false);
    },TaskContinuationoptions.OnlyOnRanToCompletion);
    tasks.Add(continuationTask);
    Task whenall = Task.WhenAll(tasks.ToArray());
    return whenall;
}

显示阻塞代码的Callstack

mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)  UnkNown
    mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)    UnkNown
    mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)    UnkNown
    mscorlib.dll!System.Threading.Tasks.Task.Wait(int millisecondsTimeout,System.Threading.CancellationToken cancellationToken)    UnkNown
    mscorlib.dll!System.Threading.Tasks.Task.Wait() UnkNown

最好的祝福

解决方法

好吧,让我们试着深入了解这里发生的事情.

首先要做的事情是:传递给您的ContinueWith的lambda的差异是微不足道的:在功能上,这部分在两个例子中是相同的(至少就我所见).

这是我用于测试的FooAsync实现:

static Task FooAsync()
{
    return Task.Delay(500);
}

我发现很奇怪的是,使用这个实现你的IndefinitelyBlockingTask花费的时间是TaskWhichWorks的两倍(分别为1秒vs~500 ms).显然,由于Unwrap,行为已经改变.

有敏锐眼光的人可能会立即发现问题,但我个人不会使用任务延续或解开那么多,所以花了一点时间沉入其中.

下面是踢球者:除非你在两种情况下都使用Unwrap继续,否则ContinueWith安排的任务会同步完成(并立即完成 – 无论循环内创建的任务有多长时间).在lambda(Task.WhenAll(childTasks.ToArray())中创建的任务,让我们称之为内部任务)以一种即发即忘的方式进行调度并运行.

展开从ContinueWith返回的任务意味着内部任务不再是一劳永逸 – 它现在是执行链的一部分,当你将它添加到列表中时,外部任务(Task.WhenAll(tasks.ToArray( )))在内部任务完成之前无法完成).

使用ContinueWith(async()=> {})不会改变上述行为,因为async lambda返回的任务不会自动解包(想想

// These two have similar behavIoUr and
// are interchangeable for our purposes.
Task.Run(() => Task.Delay(500))
Task.Run(async () => await Task.Delay(500));

VS

Task.Factory.StartNew(() => Task.Delay(500))

Task.Run调用内置Unwrap(见http://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs#0fb2b4d9262599b9#references); StartNew调用没有,它返回的任务只是立即完成而不等待内部任务.在这方面,ContinueWith类似于StartNew.

边注

重现使用Unwrap时观察到的行为的另一种方法是确保在循环内创建的任务(或它们的延续)附加到父级,导致父任务(由ContinueWith创建)不会转换到完成状态,直到所有子级任务已经完成.

for (int i = 1; i <= 5; i++)
{
    var ct = FooAsync().ContinueWith(_ => { },TaskContinuationoptions.AttachedToParent);
    childTasks.Add(ct);
}

回到原来的问题

在您当前的实现中,即使您等待Task.WhenAll(tasks.ToArray())作为外部方法的最后一行,该方法仍将在ContinueWith lambda内创建的任务完成之前返回.即使在ContinueWith中创建的任务永远不会完成(我的猜测就是你的生产代码中正是发生的事情),外部方法仍然会返回正常.

所以就是这样,上面代码所有出乎意料的事情都是由愚蠢的ContinueWith引起的,除非你使用Unwrap,否则它基本上会“掉头”. async / await绝不是原因或治愈方法(虽然,不可否认,它可以并且可能应该用于以更合理的方式重写您的方法 – 延续很难处理导致诸如此类的问题).

那么生产中发生了什么

以上所有这些让我相信,在您的ContinueWith lambda中旋转的任务之一中存在死锁,从而导致那个内在的Task.WhenAll永远不会在生产修剪中完成.

不幸的是,你还没有发布一个简洁的问题重复(我想我可以为你做这些信息,但这不是我的工作),甚至生产代码,所以这是一个很大的解决方案我可以给.

您没有使用伪代码观察所描述的行为这一事实应该暗示您可能最终剥离导致问题的位.如果你认为这听起来很愚蠢,那是因为它是,这就是为什么我最终收回我原来的问题,尽管它是我偶然遇到的最奇怪的异常问题.

结论:看看你的ContinueWith lambda.

最后编辑

你坚持认为Unwrap和await做类似的事情,这是真的(不是因为它最终会混淆任务组合,而是真实的 – 至少在这个例子的目的).但是,话虽如此,你从来没有完全使用await重新创建Unwrap语义,所以这个方法的行为有什么不同呢?这是TaskWhichWorks的await,其行为类似于Unwrap示例(当应用于您的生产代码时,它也容易受到死锁问题的影响):

static async Task TaskWhichUsedToWorkButNotAnymore()
{
    List<Task> tasks = new List<Task>();
    Task task = FooAsync();
    tasks.Add(task);
    Task<Task> continuationTask = task.ContinueWith(async t =>
    {
        List<Task> childTasks = new List<Task>();
        for (int i = 1; i <= 5; i++)
        {
            var ct = FooAsync();
            childTasks.Add(ct);
        }
        Task wa = Task.WhenAll(childTasks.ToArray());
        await wa.ConfigureAwait(continueOnCapturedContext: false);
    },TaskContinuationoptions.OnlyOnRanToCompletion);
    tasks.Add(continuationTask);

    // Let's Unwrap the async/await way.
    // Pay attention to the return type.
    // The resulting task represents the
    // completion of the task started inside
    // (and returned by) the ContinueWith delegate.
    // Without this you have no reference,and no
    // way of waiting for,the inner task.
    Task unwrappedTask = await continuationTask;

    // Boom! This method Now has the
    // same behavIoUr as the other one.
    tasks.Add(unwrappedTask);

    await Task.WhenAll(tasks.ToArray());

    // Another way of "unwrapping" the
    // continuation just to drive the point home.
    // This will complete immediately as the
    // continuation task as well as the task
    // started inside,and returned by the continuation
    // task,have both completed at this point.
    await await continuationTask;
}

c# – 如果等待任务链上的’unwrap’任务,Task.Result / wait(..)无限期等待,如果使用’async / await’则成功完成的更多相关文章

  1. iOS:核心图像和多线程应用程序

    我试图以最有效的方式运行一些核心图像过滤器.试图避免内存警告和崩溃,这是我在渲染大图像时得到的.我正在看Apple的核心图像编程指南.关于多线程,它说:“每个线程必须创建自己的CIFilter对象.否则,你的应用程序可能会出现意外行为.”这是什么意思?我实际上是试图在后台线程上运行我的过滤器,所以我可以在主线程上运行HUD(见下文).这在coreImage的上下文中是否有意义?

  2. UIWebView stringByEvaluatingJavaScriptFromString在使用GCD调用时挂在iOS5.0 / 5.1上

    我在viewDidLoad中有以下代码,它在iOS4.3上正常工作,但它挂在iOS5/5.1上.在iOS5/5.1上,显示警告对话框,但无法关闭,UI线程冻结,OK按钮无法单击.这是一个bug吗?解决方法经过测试,我认为它是一个Bug,并改变使用的代码会解决

  3. ios – iPhone:一段时间后,所有动画都停止工作

    我最近有一些奇怪的行为.所有动画有时会突然停止工作.有时候一切顺利,其他时候就会发生.推送和弹出视图只是捕捉到位,UITableViewcellrow动画不起作用.该应用程序使用了很多后台线程,所以也许有东西在那里?我不能真正发布代码,因为我不知道问题在哪里.有人有同样的问题吗?解决方法你可以尝试在不同的后台线程中更新UI/animate吗?

  4. ios – 在后台线程上创建一个视图,在主线程中添加主视图

    我是C的新来的,来自.NET和java背景.所以我需要异步地创建一些UIwebviews,我在自己的队列中使用这个因为你会想象这会引发错误:那么如何在主线程上添加子视图?

  5. 在iOS模拟器上显示GMSMarkers时发生GMSThreadException

    我正在开发一个应用程序,在GMSMapView上显示大约200个GMSMarkers我尝试了2种方法来显示标记.Method1有点慢,但没有错误发生,但是,Method2在真实设备上运行顺畅但我在iOS模拟器上测试它时得到了GMSThreadException以下是问题:1.继续使用method2可以吗?任何帮助是赞赏OrzUPDATE1正如@ztan在下面回答的那样,我必须在主线程中完成所有这些,有没有比这更好的解决方案?

  6. ios – 在分离的线程问题中使用块的异步FB请求

    我正在使用IOSFacebookSDK3,我正在尝试使用更高效的方法.所以我想在单独的线程中管理一些请求.例如这个请求:>我正在使用这个在我的Feed上发布内容,我调用一个方法来自动加载此请求的内容,然后在方法中调用此块以启动请求.这个很好用.>问题是如果我不将此请求放在一个块中,那就不起作用了.此请求不起作用我想弄清楚,但我不明白是什么问题.在此先感谢您的帮助.解决方法我有一点这个问题.确保在主线程上分派代码.

  7. ios – UIAlertController有时会阻止UIRefreshControl隐藏

    编辑:这是我在代码中创建刷新控件的方法:解决方法我相信它真的与tableView在UIAlertController呈现之前没有滚动回来有关.我试图设置showUpdateInfo方法的延迟,这似乎有效.我猜当用户只拉一次它需要半秒钟来显示UIAlertController检查是否有帮助.这是我的代码如果有帮助,请告诉我.

  8. 在Swift中应用Grand Central Dispatch(上

    在这两篇教程中,你会学到GCD的来龙去脉。起步libdispatch是Apple所提供的在IOS和OSX上进行并发编程的库,而GCD正是它市场化的名字。Swift中的闭包和OC中的块类似甚至于他们几乎就是可交换使用的。但OC中的块可以安全的替换成Swift中的闭包。再一次,这完全取决于GCD。QoS等级表示了提交任务的意图,使得GCD可以决定如何制定优先级。QOS_CLASS_USER_INteraCTIVE:userinteractive等级表示任务需要被立即执行以提供好的用户体验。

  9. 转 Grand Central Dispatch 基础教程:Part 1/2 -swift

    第一节将解释什么是GCD并了解几个GCD的基础函数。GettingStartedGCD是libdispatch的代名词,libdispatch代表着运行iOS与OSX的多核设备上执行并行代码的官方代码库。再有一点要记住的就是在任何GCD文档中涉及到Objective-C的块代码都是可以用Swift的闭包来替换的。举个具有线性安全性的代码示例leta=["thread-safe"]。因为,这一切都是由GCD控制的。任务的开始执行的时间完全由GCD决定。它也是唯一一个用作向UIView对象发送信息或推送监听。

  10. Swift之dispatch_source实现多线程定时关闭功能

    由于在项目中需要用到定时关闭音频功能,本来打算用NSTimer的,可是写起来并不是那么精简好用,所以又在网上找到相关的实例,结合自己项目需要,就写出了如下代码,还请大家指教,废话不多说:

随机推荐

  1. c# – (wpf)Application.Current.Resources vs FindResource

    所以,我正在使用C#中的WPF创建一个GUI.它看起来像这样:它现在还没有完成.这两行是我尝试制作一种数据表,它们在XAML中是硬编码的.现在,我正在C#中实现添加新的水果按钮功能.我在XAML中有以下样式来控制行的背景图像应该是什么样子:因此,在代码中,我为每列col0,col1和col2创建一个图像,如果我使用以下代码,它添加了一个如下所示的新行:如你所见,它不太正确……为什么一个似乎忽略了一些属性而另一个没有?

  2. c# – 绑定DataGridTemplateColumn

    似乎我已经打了个墙,试图在DataGrid上使用DataTemplates.我想要做的是使用一个模板来显示每个单元格的两行文本.但是似乎无法以任何方式绑定列.以下代码希望显示我想做的事情.注意每个列的绑定:模板列没有这样的东西,因此,这个xaml不可能工作.我注定要将整个DataTemplate复制到每个列,只是对每个副本都有不同的约束?解决方法我不完全确定你想要做什么,但如果您需要获取整行的DataContext,可以使用RelativeSource绑定来移动视觉树.像这样:

  3. c# – 学习设计模式的资源

    最近我来到了这个设计模式的概念,并对此感到非常热情.你能建议一些帮助我深入设计模式的资源吗?

  4. c# – 是否有支持嵌入HTML页面的跨操作系统GUI框架?

    我想开发一个桌面应用程序来使用跨系统,是否有一个GUI框架,允许我为所有3个平台编写一次代码,并具有完全可脚本化的嵌入式Web组件?我需要它有一个API来在应用程序和网页之间进行交流.我知道C#,JavaScript和一些python.解决方法Qt有这样的事情QWebView.

  5. c# – 通过字符串在对象图中查找属性

    我试图使用任意字符串访问嵌套类结构的各个部分.给出以下(设计的)类:我想要从Person对象的一个实例的“PersonsAddress.HousePhone.Number”获取对象.目前我正在使用反思来做一些简单的递归查找,但是我希望有一些忍者有更好的想法.作为参考,这里是我开发的(crappy)方法:解决方法您可以简单地使用标准的.NETDataBinder.EvalMethod,像这样:

  6. c# – 文件下载后更新页面

    FamilyID=0a391abd-25c1-4fc0-919f-b21f31ab88b7&displaylang=en&pf=true它呈现该页面,然后使用以下元刷新标签来实际向用户提供要下载的文件:你可能需要在你的应用程序中做类似的事情.但是,如果您真的有兴趣在文件完全下载后执行某些操作,那么您的运气不佳,因为没有任何事件可以与浏览器进行通信.执行此操作的唯一方法是上传附件时使用的AJAXupload.

  7. c# – 如何在每个机器应用程序中实现单个实例?

    我必须限制我的.net4WPF应用程序,以便每台机器只能运行一次.请注意,我说每个机器,而不是每个会话.我使用一个简单的互斥体实现单实例应用程序,直到现在,但不幸的是,这样一个互斥是每个会话.有没有办法创建机器互连,还是有其他解决方案来实现每个机器应用程序的单个实例?

  8. c# – WCF和多个主机头

    我的雇主网站有多个主机名,都是同一个服务器,我们只是显示不同的皮肤来进行品牌宣传.不幸的是,在这种情况下,WCF似乎不能很好地工作.我试过overridingthedefaulthostwithacustomhostfactory.这不是一个可以接受的解决方案,因为它需要从所有主机工作,而不仅仅是1.我也看过thisblogpost,但是我无法让它工作,或者不是为了解决我的问题.我得到的错误是“这

  9. c# – ASP.NET MVC模型绑定与表单元素名称中的虚线

    我一直在搜索互联网,试图找到一种方式来容纳我的表单元素的破折号到ASP.NET的控制器在MVC2,3或甚至4中的默认模型绑定行为.作为一名前端开发人员,我更喜欢在我的CSS中使用camelCase或下划线进行破折号.在我的标记中,我想要做的是这样的:在控制器中,我会传入一个C#对象,看起来像这样:有没有办法通过一些正则表达式或其他行为来扩展Controller类来适应这种情况?我讨厌这样的事实,我必须这样做:甚至这个:思考?

  10. c# – 用户界面设计工具

    我正在寻找一个用户界面设计工具来显示文档中可能的GUI.我不能生成代码.我知道MicrosoftVisio提供了一个功能.但有什么办法吗?您使用哪种软件可视化GUI?

返回
顶部