在我的求职面试中,我被赋予了一项任务,即通过一些长时间运行的方法创建异步包装器,处理一些数据,但创建它以便一次只能运行一个任务。我对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)
的调用?
答案 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}构造。
所以,回到你的问题:如果你面临一个任务陈述
一次只能运行一个任务
不是lock
(Monitor
),而是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;
}
}
}