我一直在研究(包括查看关于此主题的所有其他SO帖子)实现(最有可能)Windows服务工作者的最佳方法,该工作人员将从数据库中提取工作项并在“a'中异步并行处理它们在后台进行“即发即忘”的方式(工作项管理将全部在异步方法中处理)。工作项将是Web服务调用和数据库查询。将对这些工作项的生产者应用一些限制,以确保某种测量方法来安排工作。下面的示例非常基础,只是突出显示while循环和for循环的逻辑。哪种理想方法还是无关紧要?是否有更合适/更有效的方法来实现这一目标?
异步/ AWAIT ...
private static int counter = 1;
static void Main(string[] args)
{
Console.Title = "Async";
Task.Run(() => AsyncMain());
Console.ReadLine();
}
private static async void AsyncMain()
{
while (true)
{
// Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
var x = DoSomethingAsync(counter.ToString());
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static async Task<string> DoSomethingAsync(string jobNumber)
{
try
{
// Simulated mostly IO work - some could be long running
await Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
return "fire and forget so not really interested";
}
... Task.Run
private static int counter = 1;
static void Main(string[] args)
{
Console.Title = "Task";
while (true)
{
// Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
var x = Task.Run(() => { DoSomethingAsync(counter.ToString()); });
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static string DoSomethingAsync(string jobNumber)
{
try
{
// Simulated mostly IO work - some could be long running
Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
return "fire and forget so not really interested";
}
答案 0 :(得分:3)
从数据库中提取工作项,并在“即发即忘”的情况下异步并行处理它们。在后台的方式
从技术上讲,您需要并发。您是否希望异步并发或并行并发仍有待观察......
工作项将是Web服务调用和数据库查询。
这项工作是I / O绑定的,因此暗示异步并发是更自然的方法。
将对这些工作项的生产者进行一些限制,以确保某种测量方法来安排工作。
这里暗示了生产者/消费者队列的想法。这是一个选择。 TPL Dataflow提供了一些很好的生产者/消费者队列,它们是异步兼容的并支持限制。
或者,您可以自己进行限制。对于异步代码,有一个名为 Width="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBox}},Path=ActualWidth}"
的内置限制机制。
TPL数据流方法,带限制:
SemaphoreSlim
手动限制的异步并发方法:
private static int counter = 1;
static void Main(string[] args)
{
Console.Title = "Async";
var x = Task.Run(() => MainAsync());
Console.ReadLine();
}
private static async Task MainAsync()
{
var blockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 7
};
var block = new ActionBlock<string>(DoSomethingAsync, blockOptions);
while (true)
{
var dbData = await ...; // Imagine calling a database to get some work items to do, in this case 5 dummy items
for (int i = 0; i < 5; i++)
{
block.Post(counter.ToString());
counter++;
Thread.Sleep(50);
}
Thread.Sleep(1000);
}
}
private static async Task DoSomethingAsync(string jobNumber)
{
try
{
// Simulated mostly IO work - some could be long running
await Task.Delay(5000);
Console.WriteLine(jobNumber);
}
catch (Exception ex)
{
LogException(ex);
}
Log("job {0} has completed", jobNumber);
}
作为最后一点,我几乎没有推荐过my own book,但我确实认为这对你有好处。特别是,第8.10节(阻塞/异步队列),11.5(限制)和4.4(限制数据流块)。
答案 1 :(得分:0)
首先,让我们解决一些问题。
在第二个示例中,您正在调用
Task.Delay(5000);
没有await
。这是个坏主意。它创建一个新的Task
实例,运行5秒但没有人在等待它。 Task.Delay
仅适用于await
。请注意,不要使用Task.Delay(5000).Wait()
,否则您将陷入僵局。
在您的第二个示例中,您尝试将DoSomethingAsync
方法设为同步,我们将其称为DoSomethingSync
并将Task.Delay(5000);
替换为Thread.Sleep(5000);
现在,第二个例子几乎是老派ThreadPool.QueueUserWorkItem
。如果您没有在内部使用某些已经异步的API,那么它没有什么不妥。在“即发即弃”案件中使用的Task.Run
和ThreadPool.QueueUserWorkItem
也是一样的。为了清楚起见,我会使用后者。
这慢慢驱使我们回答主要问题。 异步或非异步 - 这就是问题!我想说:&#34;如果您不必在代码中使用某些异步IO,请不要创建异步方法&#34;。但是,如果您必须使用异步API,那么那些将在几年后阅读您的代码的人会更期望使用第一种方法。