async一次等待一个任务

时间:2017-03-10 22:30:03

标签: c# multithreading asynchronous

在我的求职面试中,我被赋予了一项任务,即通过一些长时间运行的方法创建异步包装器,处理一些数据,但创建它以便一次只能运行一个任务。我对async/await模式并不是很熟悉,所以我尽力而为,在任务风格和事件风格之间写了一些混合,以便我的包装器正在执行当前正在执行的任务,并公开一个公共方法和一个公共事件。方法将数据作为参数进行处理,如果没有任务正在运行,则启动一个任务,如果有任务,则将数据排入队列。任务在完成后提升了公共事件,即向订阅者发送流程结果并在任何入队时开始新任务。

所以,正如你可能猜到的那样,我没有接受采访,但是现在我做了一些研究,我试图找出如何正确这样做(它应该也是是线程安全的,但我太忙于担心这一点)。所以我的问题是,如果我有

public class SynchronousProcessor
{
    public string Process(string arg)
    {
        Thread.Sleep(1500); //Work imitation
        return someRandomString;
    }
}

public class AsynchronousWrapper
{
    SynchronousProcessor proc = new SynchronousProcessor();

    public async Task<string> ProcessAsync(string arg)
    {
        return Task.Run(() => proc.Process(arg));
    } 
}

,或类似的东西,如果已经有任务执行,如何正确处理对ProcessAsync(string)的调用?

3 个答案:

答案 0 :(得分:3)

许多求职面试问题的目的不仅仅是为了看你写代码。通常,问题有点含糊不清,看看你问的澄清问题 - 你的问题决定了你的表现。在白板上编写代码至多是次要的。

  

我被赋予了一项任务,即通过一些长时间运行的方法创建异步包装器,处理一些数据

第一个问题:这个长期运行的方法是异步的吗?如果是这样,那么就不需要Task.Run。但如果不是......

后续问题:如果它不是异步的,它应该是吗?即,它是基于I / O的吗?如果是这样,那么我们可以投入时间使其正确异步。但如果不是......

后续问题:如果我们需要一个任务包装器(围绕基于CPU的代码或阻塞I / O代码),环境是否适合包装器?即,这是一个桌面/移动应用程序,而不是将在ASP.NET中使用的代码吗?

  

创建它,以便一次只能运行一个任务。

澄清问题:如果第二个请求已经运行,第二个请求是否排队&#34;?或者它会&#34;合并&#34;有现有的要求?如果合并,他们是否需要&#34;键&#34;关闭输入数据 - 或输入数据的某个子集?

这些问题中的每一个都会改变答案的结构。

  

公开公共方法和公共事件。

这可能是什么扔了。在Task<T> / IProgress<T>和Rx之间,很少需要事件。如果你是一个不会学习Rx的团队,那么他们真的应该被使用。

哦,不要担心&#34;失败&#34;一个采访。我&#34;失败&#34;在我的职业生涯中,超过2/3的采访。我只是不好好采访。

答案 1 :(得分:1)

正如@MickyD已经说过,你需要知道Best Practices in Asynchronous Programming以正确的方式解决这些问题。您的解决方案具有代码气味,因为它提供async wrapper with Task.Run for a synchronous code。当您被问及图书馆开发时,它会对您的图书馆消费者产生很大的影响。

您必须了解asynchronous不是multithreading,因为可以使用一个主题进行操作。这就像等待邮件一样 - you don't hire a worker to wait by the mailbox

此处的其他解决方案并非async,因为打破async代码的其他规则:不会阻止异步操作,因此您应该避免使用{{1}构造。

所以,回到你的问题:如果你面临一个任务陈述

  

一次只能运行一个任务

不是lockMonitor),而是Semaphore(Slim)。如果由于某种原因将来您需要改进代码,以便可以同时执行多个任务,那么您将不得不重写代码。如果lock使用,您只需要更改一个常量。它还有一个用于等待方法的Semaphore包装器

所以你的代码可以是这样的(请注意,async已被删除,因为提供等待是客户的责任):

Task.Run

答案 2 :(得分:0)

这取决于你想得到多少花哨。一种简单的方法是存储任务,并链接后续任务(稍微同步):

public class AsynchronousWrapper
{
    private Task previousTask = Task.CompletedTask;     
    private SynchronousProcessor proc = new SynchronousProcessor();

    public Task<string> ProcessAsync(string arg)
    {
        lock (proc)
        {
            var task = previousTask.ContinueWith(_ => proc.Process(arg));

            previousTask = task;

            return task;
        }           
    } 
}