我的代码真的是异步执行的吗?
Is my code really executed asynchronously?
我在列表中有几个对象。每分钟,对于他们每个人,我都必须下载 api 信息并将接收到的数据保存到数据库中。
列表中的对象是相互独立的。每个对象的方法可以单独调用。根据 api 的响应时间,单个方法的总执行时间通常在 ~0.5 到 5 秒之间。
根据 Whosebug 上其他线程中的建议,我创建了以下代码:
private async void DownloadAndSaveDataForMyObjects(object state)
{
try
{
await Task.Run(() => Parallel.ForEach(MyObjectsList, ServiceSingleObjectMethod));
}
}
private void ServiceSingleObjectMethod(RehDeviceSmsRequest myObjectFromList)
{
var apiInfo = GetInfoFromApi(myObjectFromList);
SaveInfoToDatabase(myObjectFromList);
}
在我看来代码是异步的。但是……我有些疑惑。记录器通知我在应用程序期间执行的操作 running.The 对象方法调用之间的间隔非常大。请看这个:
[13:32:46 DBG][MyService]->MyMethod => Update object id: 54
[13:32:47 DBG][MyService]->MyMethod => Update object id: 9
[13:32:50 DBG][MyService]->MyMethod => Update object id: 47
[13:32:51 DBG][MyService]->MyMethod => Update object id: 21
[13:32:51 DBG][MyService]->MyMethod => Update object id: 53
[13:32:53 DBG][MyService]->MyMethod => Update object id: 37
[13:32:55 DBG][MyService]->MyMethod => Update object id: 62
[13:32:55 DBG][MyService]->MyMethod => Update object id: 63
[13:32:56 DBG][MyService]->MyMethod => Update object id: 64
[13:32:56 DBG][MyService]->MyMethod => Update object id: 55
[13:32:56 DBG][MyService]->MyMethod => Update object id: 36
[13:32:56 DBG][MyService]->MyMethod => Update object id: 30
[13:32:56 DBG][MyService]->MyMethod => Update object id: 46
[13:32:56 DBG][MyService]->MyMethod => Update object id: 26
[13:32:56 DBG][MyService]->MyMethod => Update object id: 56
[13:33:00 DBG][MyService]->MyMethod => Update object id: 29
[13:33:00 DBG][MyService]->MyMethod => Update object id: 57
[13:33:00 DBG][MyService]->MyMethod => Update object id: 42
[13:33:01 DBG][MyService]->MyMethod => Update object id: 38
[13:33:01 DBG][MyService]->MyMethod => Update object id: 32
[13:33:01 DBG][MyService]->MyMethod => Update object id: 48
[13:33:01 DBG][MyService]->MyMethod => Update object id: 40
[13:33:01 DBG][MyService]->MyMethod => Update object id: 31
[13:33:01 DBG][MyService]->MyMethod => Update object id: 49
[13:33:02 DBG][MyService]->MyMethod => Update object id: 58
[13:33:02 DBG][MyService]->MyMethod => Update object id: 9
[13:33:02 DBG][MyService]->MyMethod => Update object id: 33
[13:33:02 DBG][MyService]->MyMethod => Update object id: 41
[13:33:04 DBG][MyService]->MyMethod => Update object id: 50
[13:33:04 DBG][MyService]->MyMethod => Update object id: 34
[13:33:06 DBG][MyService]->MyMethod => Update object id: 17
[13:33:06 DBG][MyService]->MyMethod => Update object id: 59
[13:33:07 DBG][MyService]->MyMethod => Update object id: 45
[13:33:08 DBG][MyService]->MyMethod => Update object id: 61
[13:33:08 DBG][MyService]->MyMethod => Update object id: 54
[13:33:08 DBG][MyService]->MyMethod => Update object id: 43
[13:33:08 DBG][MyService]->MyMethod => Update object id: 51
[13:33:08 DBG][MyService]->MyMethod => Update object id: 52
处理后续对象之间的间隔非常大。我以为我的异步代码会让每个对象的方法同时被调用(或者非常非常相似)...
如何确保我的代码是异步的并且工作正常?
你能确认我为每个对象正确调用任务吗?
Parallel.ForEach
不会主动从线程池中释放线程。 任务调度程序 将决定您应该使用多少个线程,并且根据内核、您的系统已经在做什么以及它从您的任务中收集的启发式方法,它可能不会做什么你会期待的。
但是,您在这里有更大的概念问题。您正在使用 Parallel.ForEach
(看起来是)面包和黄油 IO 绑定工作,这是 tying-up/blocking 线程,可以更适当地卸载到 IO 完成端口,进而有更大的机会实现更多并发IO 绑定工作负载。
简而言之,Parallel.ForEach
不适合 IO 绑定工作负载,它针对 CPU 绑定工作负载进行了优化 并且不支持 async 和 await 模式.
总而言之,您应该让您的 IO 方法成为 async(一直向下)并使用更合适的技术可以处理 async 和 await 模式,例如 Task.WhenAll
或 TPL DataFlow ActionBlock<T>
或 Reactive Extensions 。此外,您可能应该将您的工作分批处理到 数据库 ,这样您就不会为此而烦恼。这将为您的 工作负载 提供最好的机会,而不是无所事事地阻塞 线程池 线程,反过来您可能会发现 through-put(一般来说)会高很多。
我在列表中有几个对象。每分钟,对于他们每个人,我都必须下载 api 信息并将接收到的数据保存到数据库中。
列表中的对象是相互独立的。每个对象的方法可以单独调用。根据 api 的响应时间,单个方法的总执行时间通常在 ~0.5 到 5 秒之间。
根据 Whosebug 上其他线程中的建议,我创建了以下代码:
private async void DownloadAndSaveDataForMyObjects(object state)
{
try
{
await Task.Run(() => Parallel.ForEach(MyObjectsList, ServiceSingleObjectMethod));
}
}
private void ServiceSingleObjectMethod(RehDeviceSmsRequest myObjectFromList)
{
var apiInfo = GetInfoFromApi(myObjectFromList);
SaveInfoToDatabase(myObjectFromList);
}
在我看来代码是异步的。但是……我有些疑惑。记录器通知我在应用程序期间执行的操作 running.The 对象方法调用之间的间隔非常大。请看这个:
[13:32:46 DBG][MyService]->MyMethod => Update object id: 54
[13:32:47 DBG][MyService]->MyMethod => Update object id: 9
[13:32:50 DBG][MyService]->MyMethod => Update object id: 47
[13:32:51 DBG][MyService]->MyMethod => Update object id: 21
[13:32:51 DBG][MyService]->MyMethod => Update object id: 53
[13:32:53 DBG][MyService]->MyMethod => Update object id: 37
[13:32:55 DBG][MyService]->MyMethod => Update object id: 62
[13:32:55 DBG][MyService]->MyMethod => Update object id: 63
[13:32:56 DBG][MyService]->MyMethod => Update object id: 64
[13:32:56 DBG][MyService]->MyMethod => Update object id: 55
[13:32:56 DBG][MyService]->MyMethod => Update object id: 36
[13:32:56 DBG][MyService]->MyMethod => Update object id: 30
[13:32:56 DBG][MyService]->MyMethod => Update object id: 46
[13:32:56 DBG][MyService]->MyMethod => Update object id: 26
[13:32:56 DBG][MyService]->MyMethod => Update object id: 56
[13:33:00 DBG][MyService]->MyMethod => Update object id: 29
[13:33:00 DBG][MyService]->MyMethod => Update object id: 57
[13:33:00 DBG][MyService]->MyMethod => Update object id: 42
[13:33:01 DBG][MyService]->MyMethod => Update object id: 38
[13:33:01 DBG][MyService]->MyMethod => Update object id: 32
[13:33:01 DBG][MyService]->MyMethod => Update object id: 48
[13:33:01 DBG][MyService]->MyMethod => Update object id: 40
[13:33:01 DBG][MyService]->MyMethod => Update object id: 31
[13:33:01 DBG][MyService]->MyMethod => Update object id: 49
[13:33:02 DBG][MyService]->MyMethod => Update object id: 58
[13:33:02 DBG][MyService]->MyMethod => Update object id: 9
[13:33:02 DBG][MyService]->MyMethod => Update object id: 33
[13:33:02 DBG][MyService]->MyMethod => Update object id: 41
[13:33:04 DBG][MyService]->MyMethod => Update object id: 50
[13:33:04 DBG][MyService]->MyMethod => Update object id: 34
[13:33:06 DBG][MyService]->MyMethod => Update object id: 17
[13:33:06 DBG][MyService]->MyMethod => Update object id: 59
[13:33:07 DBG][MyService]->MyMethod => Update object id: 45
[13:33:08 DBG][MyService]->MyMethod => Update object id: 61
[13:33:08 DBG][MyService]->MyMethod => Update object id: 54
[13:33:08 DBG][MyService]->MyMethod => Update object id: 43
[13:33:08 DBG][MyService]->MyMethod => Update object id: 51
[13:33:08 DBG][MyService]->MyMethod => Update object id: 52
处理后续对象之间的间隔非常大。我以为我的异步代码会让每个对象的方法同时被调用(或者非常非常相似)...
如何确保我的代码是异步的并且工作正常? 你能确认我为每个对象正确调用任务吗?
Parallel.ForEach
不会主动从线程池中释放线程。 任务调度程序 将决定您应该使用多少个线程,并且根据内核、您的系统已经在做什么以及它从您的任务中收集的启发式方法,它可能不会做什么你会期待的。
但是,您在这里有更大的概念问题。您正在使用 Parallel.ForEach
(看起来是)面包和黄油 IO 绑定工作,这是 tying-up/blocking 线程,可以更适当地卸载到 IO 完成端口,进而有更大的机会实现更多并发IO 绑定工作负载。
简而言之,Parallel.ForEach
不适合 IO 绑定工作负载,它针对 CPU 绑定工作负载进行了优化 并且不支持 async 和 await 模式.
总而言之,您应该让您的 IO 方法成为 async(一直向下)并使用更合适的技术可以处理 async 和 await 模式,例如 Task.WhenAll
或 TPL DataFlow ActionBlock<T>
或 Reactive Extensions 。此外,您可能应该将您的工作分批处理到 数据库 ,这样您就不会为此而烦恼。这将为您的 工作负载 提供最好的机会,而不是无所事事地阻塞 线程池 线程,反过来您可能会发现 through-put(一般来说)会高很多。