究竟有什么区别
Task.Run(() => {
LongRunningMethod();
});
Run Code Online (Sandbox Code Playgroud)
要么
HostingEnvironment.QueueBackgroundWorkItem(clt => LongRunningMethod());
Run Code Online (Sandbox Code Playgroud)
我在Asp.Net MVC应用程序上进行了测试,在该应用程序中,我在使用Task.Run或QBWI调用的异步任务中继续在文本文件中写入一行约10分钟.
使用Task和QBWI都可以.我的异步方法一直写入该文件,直到10分钟没有任何问题.我没有观察到IIS对其回收的干扰.
那么QueueBackgroundWorkItem有什么特别之处呢?
我有一些简单的代码作为repro:
var taskTest = Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(5000);
}).ContinueWith((Task t) =>
{
Console.WriteLine("ERR");
}, TaskContinuationOptions.OnlyOnFaulted);
try
{
Task.WaitAll(taskTest);
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
Console.WriteLine(e.Message + Environment.NewLine + e.StackTrace);
}
Run Code Online (Sandbox Code Playgroud)
但是,我在try catch块中抛出一个意外的TaskCanceledException(它在AggregateException InnerExceptions对象中)."任务被取消了".
为什么我得到这个例外?任务的Continuation永远不会触发,它没有生成任何异常,但在等待时我仍然得到聚合异常....
我希望有人能解释一下这对我有多大意义:)
我正在设置一些单元测试并使用Rhino Mocks填充正在测试的对象.被模拟的事情之一是Task<HttpResponseMessage>,因为被测试的逻辑包括HttpClient对获取异步响应的调用.
所以我开始设置这样的模拟:
var httpClient = MockRepository.GenerateMock<HttpClient>();
var taskFunc = MockRepository.GenerateMock<Func<HttpResponseMessage>>();
var responseTask = MockRepository.GenerateMock<Task<HttpResponseMessage>>(taskFunc);
var response = MockRepository.GenerateMock<HttpResponseMessage>();
httpClient.Stub(c => c.PostAsJsonAsync<IEnumerable<LogMessage>>(Arg<string>.Is.Anything, Arg<IEnumerable<LogMessage>>.Is.Anything)).Return(responseTask);
responseTask.Stub(t => t.Result).Return(response);
response.Stub(r => r.IsSuccessStatusCode).Return(true);
Run Code Online (Sandbox Code Playgroud)
(测试的"行为"步骤是实例化被测对象,将其提供给它httpClient,并在其上运行一个方法."断言"将通过模拟验证对它们进行了预期的方法调用.)
在调试器中单步执行此操作,此行上有一个无限期挂起:
responseTask.Stub(t => t.Result).Return(response);
Run Code Online (Sandbox Code Playgroud)
我没有很多使用Rhino Mocks或C#异步的经验,所以我可能会忽略一些明显的东西.当然,目标是对.Result财产的任何调用都将返回response模拟.但看起来我的尝试本身也许正在调用.Result我期望无限期等待,因为它只是一个模拟,也许?
安排这个的正确方法是什么?基本上我需要为我的对象提供一个模拟HttpClient并声明一个方法是用一个特定的参数调用它的.
我有一个界面
interface IFoo
{
Task<Bar> CreateBarAsync();
}
Run Code Online (Sandbox Code Playgroud)
有两种方法可以创建Bar,一种是异步,另一种是同步.我想为这两种方法中的每一种提供接口实现.
对于异步方法,实现可能如下所示:
class Foo1 : IFoo
{
async Task<Bar> CreateBarAsync()
{
return await AsynchronousBarCreatorAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我该如何实现Foo2使用同步方法创建的类Bar?
我可以实现同步运行的方法:
async Task<Bar> CreateBarAsync()
{
return SynchronousBarCreator();
}
Run Code Online (Sandbox Code Playgroud)
然后,编译器将警告不要async在方法签名中使用:
这种异步方法缺少"等待"运算符并将同步运行.考虑使用'await'运算符等待非阻塞API调用,或'await Task.Run(...)'在后台线程上执行CPU绑定工作.
或者,我可以实现显式返回的方法Task<Bar>.在我看来,代码看起来不那么可读:
Task<Bar> CreateBarAsync()
{
return Task.Run(() => SynchronousBarCreator());
}
Run Code Online (Sandbox Code Playgroud)
从性能的角度来看,我认为这两种方法的开销大致相同,或者?
我应该选择哪种方法; async同步实现方法或显式包装同步方法调用Task?
编辑
我正在开发的项目实际上是一个.NET 4项目,其中包含来自Microsoft Async NuGet包的async/await扩展.在.NET 4上,可以替换为.我有意识地在上面的例子中使用了.NET 4.5方法,希望能让主要问题更加清晰.Task.RunTaskEx.Run
我开始一项任务,开始其他任务等等.
给定该树,如果任何任务失败,则整个操作的结果是无用的.我正在考虑使用取消令牌.令我惊讶的是,令牌没有"CancelThisToken()"方法......
所以我的问题是:我怎么能只拥有一个CancellationToken取消它呢?
我有这样的方法:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
foreach(var method in Methods)
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
然后我决定使用Parallel.ForEach:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
Parallel.ForEach(Methods, async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
});
return result;
}
Run Code Online (Sandbox Code Playgroud)
但现在我有一个错误:
在异步操作仍处于挂起状态时完成异步模块或处理程序.
考虑一个包含大量需要处理的作业的队列.队列限制一次只能获得1个工作,无法知道有多少工作.这些作业需要10秒才能完成,并且需要大量等待来自Web服务的响应,因此不受CPU限制.
如果我使用这样的东西
while (true)
{
var job = Queue.PopJob();
if (job == null)
break;
Task.Factory.StartNew(job.Execute);
}
Run Code Online (Sandbox Code Playgroud)
然后,它会以比完成它们更快的速度从队列中快速弹出作业,耗尽内存并堕落.> <
我不能使用(我不认为)ParallelOptions.MaxDegreeOfParallelism因为我不能使用Parallel.Invoke或Parallel.ForEach
我找到了3个替代方案
用.替换Task.Factory.StartNew
Task task = new Task(job.Execute,TaskCreationOptions.LongRunning)
task.Start();
Run Code Online (Sandbox Code Playgroud)
这似乎在某种程度上解决了这个问题,但我不清楚这是做什么的,如果这是最好的方法.
创建一个限制并发度的自定义任务调度程序
使用类似BlockingCollection的东西在启动时将作业添加到集合中,并在完成时删除以限制可以运行的编号.
#1我必须相信自己做出正确的决定,#2 /#3我必须计算出自己可以运行的最大数量的任务.
我是否理解正确 - 这是更好的方式,还是有另一种方式?
编辑 - 这是我从下面的答案,生产者 - 消费者模式中得出的结果.
除了整体吞吐量目标不是要比可以处理的更快地使作业出列并且没有多个线程轮询队列(这里没有显示但是这是非阻塞的操作并且如果从多个地方以高频率轮询将导致巨大的交易成本) .
// BlockingCollection<>(1) will block if try to add more than 1 job to queue (no
// point in being greedy!), or is empty on take.
var BlockingCollection<Job> jobs = …Run Code Online (Sandbox Code Playgroud) 我使用控制台应用程序作为概念证明和获得异步返回值的新需求.
我发现我需要Task.WaitAll()在我的main方法中使用以避免需要异步"main()"方法,这是非法的.
我现在卡住试图弄清楚允许我使用泛型的重载,或者只返回我可以投射的对象,但是在Main()中.
.net c# console-application task-parallel-library async-await
我试图了解其目的TaskCompletionSource及其与异步/无线工作的关系.我想我有一般的想法,但我想确保我的理解是正确的.
我首先开始研究任务并行库(TPL),以确定是否有一种很好的方法来创建自己的无线/异步工作(比如说你试图提高ASP.NET站点的可伸缩性)以及对TPL的理解看起来它将来会非常重要(async/ await).这导致我TaskCompletionSource.
从我的理解来看,添加TaskCompletionSource到你的一个类似乎没有做太多的编码异步; 如果您仍在执行同步代码,则对代码的调用将被阻止.我认为微软API也是如此.例如,假设在DownloadStringTaskAsync关闭的WebClient类,它们最初在做任何安装/同步代码将被阻塞.您正在执行的代码必须在某个线程上运行,无论是当前线程还是您必须分拆一个新线程.
因此TaskCompletionSource,当您调用asyncMicrosoft的其他调用时,您可以在自己的代码中使用,这样您的类的客户端就不必为您的类创建一个新的线程来阻止它.
不确定Microsoft如何在内部执行异步API.例如,for .Net 4.5 有一种新async方法SqlDataReader.我知道有IO完成端口.我认为这可能是大多数C#开发人员不会使用的低级抽象(C++?).不确定IO完成端口是否适用于数据库或网络调用(HTTP),或者它是否仅用于文件IO.
所以问题是,我理解正确吗?是否有某些我错误表示的事情?
ConfigureAwait(false)在C#中使用await/async 时,有很多指南可供使用.
似乎一般的建议是ConfigureAwait(false)在库代码中使用,因为它很少依赖于同步上下文.
但是,假设我们正在编写一些非常通用的实用程序代码,它将函数作为输入.一个简单的例子可能是以下(不完整的)功能组合器,以简化基于任务的简单操作:
地图:
public static async Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> mapping)
{
return mapping(await task);
}
Run Code Online (Sandbox Code Playgroud)
FlatMap:
public static async Task<TResult> FlatMap<T, TResult>(this Task<T> task, Func<T, Task<TResult>> mapping)
{
return await mapping(await task);
}
Run Code Online (Sandbox Code Playgroud)
问题是,我们应该ConfigureAwait(false)在这种情况下使用吗?我不确定上下文捕获是如何工作的.关闭.
一方面,如果组合器以功能方式使用,则不需要同步上下文.另一方面,人们可能会滥用API,并在提供的函数中执行依赖于上下文的内容.
一种选择是为每个场景(Map和/ MapWithContextCapture或某些东西)设置单独的方法,但感觉很难看.
另一种选择可能是将map/flatmap选项添加到a中ConfiguredTaskAwaitable<T>,但是由于等待不必实现接口,这会导致大量冗余代码,在我看来更糟糕.
是否有一种将责任转交给调用者的好方法,这样实现的库就不需要对提供的映射函数中是否需要上下文做出任何假设?
或者仅仅是一个事实,异步方法组成得不是很好,没有各种假设?
只是为了澄清一些事情:
ConfigureAwait(false)将导致空同步.上下文.正如一些答案所提到的那样,可以在方法中添加一个bool-flag,但正如我所看到的,这也不是太漂亮,因为它必须一直传播到API中(因为它有)更多"实用"功能,取决于上面显示的功能.
c# task synchronizationcontext task-parallel-library async-await