使用Thread.Sleep等效的Parallel.ForEach [英] Parallel.ForEach using Thread.Sleep equivalent

查看:128
本文介绍了使用Thread.Sleep等效的Parallel.ForEach的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是这种情况:我需要打电话给开始搜索的网站.该搜索持续了未知的时间,而我知道搜索是否完成的唯一方法是通过定期查询网站以查看其上是否有下载数据"链接(它在javascript上使用了一些奇怪的ajax调用我认为是计时器来检查后端并更新页面).

So here's the situation: I need to make a call to a web site that starts a search. This search continues for an unknown amount of time, and the only way I know if the search has finished is by periodically querying the website to see if there's a "Download Data" link somewhere on it (it uses some strange ajax call on a javascript timer to check the backend and update the page, I think).

这就是窍门:我需要一次搜索数百个项目.所以我有一些看起来像这样的代码:

So here's the trick: I have hundreds of items I need to search for, one at a time. So I have some code that looks a little bit like this:

var items = getItems();
Parallel.ForEach(items, item =>
{
   startSearch(item);
   var finished = isSearchFinished(item);
   while(finished == false)
   {
      finished = isSearchFinished(item); //<--- How do I delay this action 30 Secs?
   }
   downloadData(item);
}

现在,显然这不是真正的代码,因为可能会导致isSearchFinished始终为false的某些事情.

Now, obviously this isn't the real code, because there could be things that cause isSearchFinished to always be false.

撇开显而易见的无限循环危险,我该如何正确地防止isSearchFinished()一遍又一遍地打电话,而是每隔30秒或1分钟打电话一次?

Obvious infinite loop danger aside, how would I correctly keep isSearchFinished() from calling over and over and over, but instead call every, say, 30 seconds or 1 minute?

我知道Thread.Sleep()不是正确的解决方案,我认为该解决方案可以通过使用Threading.Timer()来实现,但我对此并不十分熟悉,而且有很多线程选项,我只是不确定该使用哪个.

I know Thread.Sleep() isn't the right solution, and I think the solution might be accomplished by using Threading.Timer() but I'm not very familiar with it, and there are so many threading options that I'm just not sure which to use.

推荐答案

使用任务和async/await十分容易实现,如@KevinS在评论中指出的那样:

It's quite easy to implement with tasks and async/await, as noted by @KevinS in the comments:

async Task<ItemData> ProcessItemAsync(Item item)
{
    while (true)
    {
        if (await isSearchFinishedAsync(item))
            break;
        await Task.Delay(30 * 1000);
    }
    return await downloadDataAsync(item);
}

// ...

var items = getItems();
var tasks = items.Select(i => ProcessItemAsync(i)).ToArray();
await Task.WhenAll(tasks);
var data = tasks.Select(t = > t.Result);

这样,您就不会徒劳地阻塞ThreadPool线程,因为这些线程大部分都是与I/O绑定的网络操作.如果您不熟悉async/await,则 async-await 标签Wiki可能是一个不错的起点

This way, you don't block ThreadPool threads in vain for what is mostly a bunch of I/O-bound network operations. If you're not familiar with async/await, the async-await tag wiki might be a good place to start.

我假设您可以使用HttpClient之类的东西将同步方法isSearchFinisheddownloadData转换为异步版本,以实现非阻塞HTTP请求并返回Task<>.如果无法这样做,您仍然可以简单地用Task.Run将它们包装起来,如await Task.Run(() => isSearchFinished(item))await Task.Run(() => downloadData(item)).通常不建议这样做,但是由于有数百个项目,因此与Parallel.ForEach相比,它会为您提供更好的并发级别,因为由于异步Task.Delay.

I assume you can convert your synchronous methods isSearchFinished and downloadData to asynchronous versions using something like HttpClient for non-blocking HTTP request and returning a Task<>. If you are unable to do so, you still can simply wrap them with Task.Run, as await Task.Run(() => isSearchFinished(item)) and await Task.Run(() => downloadData(item)). Normally this is not recommended, but as you have hundreds of items, it sill would give you a much better level of concurrency than with Parallel.ForEach in this case, because you won't be blocking pool threads for 30s, thanks to asynchronous Task.Delay.

这篇关于使用Thread.Sleep等效的Parallel.ForEach的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆