繁体   English   中英

Task.Run是否可以扩展以及使用示例WebApi中的任务?

[英]Does Task.Run scale as well as using Tasks from example WebApi?

我们的系统中有很多请求,因此我们将Tasks与WebApi结合使用。 在某些地方,我们对速度有很高的要求,因此我们不能等待任务完成,因此我为此创建了一个Worker。 它创建了一个嵌套的容器,以便实体框架DbContext不会被丢弃等。但是它看起来像Task.Run每次都生成一个新线程,这种扩展的效果如何?

public class BackgroundWorker<TScope> : IBusinessWorker<TScope>, IRegisteredObject where TScope : class
{
    private readonly IBusinessScope<TScope> _scope;
    private bool _started;
    private bool _stopping;

    public BackgroundWorker(IBusinessScope<TScope> scope)
    {
        _scope = scope;
    }

    public void Run(Func<TScope, Task> action)
    {
        if(_stopping) throw new Exception("App pool is recycling, cant queue work");
        if(_started) throw new Exception("You cant call Run multiple times");

        _started = true;

        HostingEnvironment.RegisterObject(this);

        Task.Run(() =>
            action(_scope.EntryPoint).ContinueWith(t =>
            {
                _scope.Dispose();
                HostingEnvironment.UnregisterObject(this);
            }));
    }

    public void Stop(bool immediate)
    {
        _stopping = true;

        if(immediate)
            HostingEnvironment.UnregisterObject(this);
    }
}

使用像

backgroundWorker.Run(async ctx => await ctx.AddRange(foos).Save());

如果我用谷歌搜索,它们最终都将使用Task.Run但这不是杀死目的吗?

更新:做了测试

var guid = Guid.NewGuid();

_businessWorker.Run(async ctx => {
    System.Diagnostics.Debug.WriteLine("{0}: {1}", guid, Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(1);
    System.Diagnostics.Debug.WriteLine("{0}: {1}", guid, Thread.CurrentThread.ManagedThreadId);
});

这个输出

3bdbe90b-c31e-4709-95d8-f7516210b0ac: 17
3bdbe90b-c31e-4709-95d8-f7516210b0ac: 9
6548fd26-d209-4427-9a91-40fc30aa509e: 15
6548fd26-d209-4427-9a91-40fc30aa509e: 19
7411b043-4fae-44bf-b93f-4273a532afa1: 7
7411b043-4fae-44bf-b93f-4273a532afa1: 17

这表明Task.Run实际上像我认为的那样工作

使用真实的数据库代码,看起来像这样

a939713d-d728-46c9-be33-aa57704cf242: 19 <--
a939713d-d728-46c9-be33-aa57704cf242: 19 <-- Used same for entire work
7e588a42-afd0-4ab5-ba6b-f8520c889cde: 7
7e588a42-afd0-4ab5-ba6b-f8520c889cde: 19 <-- Reused first works thread when work #2 continued
6f3b067f-f478-43f9-8411-8142b449c28b: 8
6f3b067f-f478-43f9-8411-8142b449c28b: 18

更新:尝试了Luaan的方法,似乎可以使用从EntityFramework或WebApi HttpClient生成的Tasks,但是对于下面这样的手动Tasks来说效果不佳,有些被执行,有些则没有。 使用Task.Run都可以执行

_businessWorkerFactory().Run(async ctx =>
{
    var guid = Guid.NewGuid();
    System.Diagnostics.Debug.WriteLine("{0}: {1}", guid, Thread.CurrentThread.ManagedThreadId);

    var completion = new TaskCompletionSource<bool>();

    ThreadPool.QueueUserWorkItem(obj =>
    {
        Thread.Sleep(1000);
        completion.SetResult(true);
    });

    await completion.Task;

    System.Diagnostics.Debug.WriteLine("{0}: {1}", guid, Thread.CurrentThread.ManagedThreadId);
});

Task.Run计划任务在线程池线程上运行。 处理请求的相同线程池。

在ASP.NET应用程序上,将工作发送到线程池会窃取处理请求所需的线程。

鉴于您的需求,我认为您最好使用MSMQ之类的方法将其排队到另一个服务/进程。

Task.Run不会产生新线程-它从线程池中借用一个线程(假设线程池任务调度程序-有不同的调度程序,您也可以编写自己的线程)。 当您在Task.Run内部使用await时,它仍将照常工作-释放线程池线程,直到发布回调为止。

但是,正是由于这个原因,使用Task.Run进行I / O工作毫无意义。 如果您要执行异步I / O,则只需执行它-它的工作原理完全相同,而无需上下文切换。 但是,您必须使其异步-如果它只是阻塞代码,那么您将从线程池中占用宝贵的线程。

请注意,您不需要为异步请求的完成。 如果您执行的异步操作不需要太多时间进行设置(也就是说,即使尚未完成,它几乎立即返回Task ),您可以直接调用它:

public async Task SomeAsync()
{
  var request = new MyRequest();

  await request.MakeRequestAsync();

  ...
}

public void Start()
{
  var task = SomeAsync();

  // Now the task is started, and we can use it for future reference. Or just wire up
  // some error handling continuations etc. - though it's usually a better idea to do that
  // within SomeAsync directly.
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM