C# Task.Wheall未完成
我不熟悉C#中的任务,遇到了一个我不理解的问题。在下面的代码中,有人可以解释为什么Unsubscribe方法末尾的WriteLine从未被调用。它似乎与前面foreach循环中的ContinueWith有关,好像我评论说它工作得很好。 谢谢C# Task.Wheall未完成,c#,multithreading,task,task-parallel-library,C#,Multithreading,Task,Task Parallel Library,我不熟悉C#中的任务,遇到了一个我不理解的问题。在下面的代码中,有人可以解释为什么Unsubscribe方法末尾的WriteLine从未被调用。它似乎与前面foreach循环中的ContinueWith有关,好像我评论说它工作得很好。 谢谢 使用系统; 使用系统线程; 使用System.Linq; 使用System.Threading.Tasks; 使用System.Collections.Generic; 公开课范例 { 公共静态void Main() { Task.Run(()=>Unsub
使用系统;
使用系统线程;
使用System.Linq;
使用System.Threading.Tasks;
使用System.Collections.Generic;
公开课范例
{
公共静态void Main()
{
Task.Run(()=>Unsubscribe());
Console.WriteLine(“方法:Main.End”);
Console.ReadKey();
}
私有静态无效取消订阅()
{
Dictionary dicPairsBySubID=新字典();
添加(1,“A”);
添加(2,“B”);
添加(3,“C”);
添加(4,“D”);
添加(5,“E”);
List taskList=新列表();
foreach(dicPairsBySubID中的KeyValuePair子对象)
{
int chanID=子密钥;
Task Task=Task.Run(()=>SendUnsubscribe(chanID))
.ContinueWith(tsk=>
{
var Flatten=tsk.Exception.Flatten();
展平.句柄(ex=>
{
WriteLine(string.Format(“错误取消订阅对{0}(chanID={2})”,sub.Value,chanID),ex);
返回true;
});
},TaskContinuationOptions.OnlyOnFaulted);
任务列表。添加(任务);
}
Task.WhenAll(taskList.Wait();
//为什么这个从来没有运行过?
Console.WriteLine(“方法:Unsubscribe.End”);
}
专用静态异步任务SendUnsubscribe(int chanID)
{
等待发送异步(chanID);
Console.WriteLine(“方法:SendUnsubscribe.Chan ID:+chanID”);
}
专用静态异步任务SendAsync(int chanID)
{
等待任务。运行(()=>Thread.Sleep(1000));
Console.WriteLine(“方法:SendAsync.Chan ID:+chanID”);
}
}
我对此进行了测试。我在收到一个异常,当时所有的都说一个任务被取消了。这一切都是有道理的:
正如Default的回答所指出的,您正在将ContinueWith
任务添加到任务列表中
ContinueWith
任务将被取消,因为它应该仅在出现故障的情况下运行,并且没有发生异常
由于引发(且未处理)异常,因此Unsubscribe
的其余部分不会运行
以下是一些可以改进的方法:
不要使用Task.Run来运行异步方法。您可以这样做:
taskList.Add(SendUnsubscribe(chanID))代码>
在SendUnsubscribe
方法中使用try
/catch
,而不是在任务中使用ContinueWith
等待whalll
而不是使用Wait()
:
wait Task.WhenAll(任务列表)代码>
您可以始终包装wait Task.WhenAll(任务列表)如果您认为任何异常都可能发生,而发送取消订阅
无法处理,请在中尝试/捕获
块中的code>。您的任务会抛出一个未捕获的异常,因为:
ContinueWith
还返回一个任务(然后将其放入任务列表中
)
- 您已经声明,仅当上一个任务出现故障时才应运行该任务(由于
TaskContinuationOptions.OnlyOnFaulted
)
- 您给出的示例未进入故障状态
- 当由于这些选项而未运行ContinueWith任务时,它将
- 等待已取消的任务会引发异常
- 在Main方法中,
Task.Run(()=>Unsubscribe())
调用的返回值不是wait
ed,因此不会被注意到
进一步说明:
- 您不应该在任务上使用
Wait()
。您已经开始使用async
和wait
,请通过程序使用它。
真正使用Wait()。一定要尽量避免
- 当方法是
Async
时,请始终使用后缀Async
命名方法。这显示了使用该方法的用户的明确意图
如果要使用异步,请正确使用。使用C#7.1和async main
。不要使用.ContinueWith()
或.Wait()
。那些是代码气味。您需要等待异步调用。仔细阅读。你不需要所有这些任务。运行s。特别是,当SendUnsubscribe已经是一个任务,甚至不等待它的结果或异常时,将其包装在一个中有什么意义<代码>任务.WhenAll(任务列表).Wait()代码>也可以替换为等待任务.WhenAll(任务列表)通过声明专用异步任务Unsubscribe()
执行code>。这与问题无关。将延续标记为OnlyOnFaulted
并不意味着它永远不会完成如果先行项没有出现故障,这只意味着委托没有运行,任务也被标记为完成。问题并不是问为什么该延续中的代码没有运行,而是问为什么所有的
都没有完成,而这与该延续是只在有故障的任务上运行,还是这些任务有故障无关。@谢谢您的输入!我在实际尝试代码后编辑了这个问题。现在应该更正确了(举一个例子)@Servy这个答案还有什么问题吗?谢谢你的默认,谢谢你的“进一步”,我很感激你的建议。我想我正在慢慢开始掌握任务和异步/等待的诀窍。
using System;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
Task.Run(() => Unsubscribe());
Console.WriteLine("Method: Main. End");
Console.ReadKey();
}
private static void Unsubscribe()
{
Dictionary<int, string> dicPairsBySubID = new Dictionary<int, string>();
dicPairsBySubID.Add(1, "A");
dicPairsBySubID.Add(2, "B");
dicPairsBySubID.Add(3, "C");
dicPairsBySubID.Add(4, "D");
dicPairsBySubID.Add(5, "E");
List<Task> taskList = new List<Task>();
foreach (KeyValuePair<int, string> sub in dicPairsBySubID)
{
int chanID = sub.Key;
Task task = Task.Run(() => SendUnsubscribe(chanID))
.ContinueWith(tsk =>
{
var flattened = tsk.Exception.Flatten();
flattened.Handle(ex =>
{
Console.WriteLine(string.Format("Error unsubscribing pair {0} (chanID = {2})", sub.Value, chanID), ex);
return true;
});
}, TaskContinuationOptions.OnlyOnFaulted);
taskList.Add(task);
}
Task.WhenAll(taskList).Wait();
// WHY DOES THIS NEVER GET RUN?
Console.WriteLine("Method: Unsubscribe. End");
}
private static async Task SendUnsubscribe(int chanID)
{
await SendAsync(chanID);
Console.WriteLine("Method: SendUnsubscribe. Chan ID: " + chanID);
}
private static async Task SendAsync(int chanID)
{
await Task.Run(() => Thread.Sleep(1000));
Console.WriteLine("Method: SendAsync. Chan ID: " + chanID);
}
}