本文介绍了并行运行异步方法 8 次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将以下内容转换为 Parallel.ForEach?

How do I turn the following into a Parallel.ForEach?

public async void getThreadContents(String[] threads)
{
    HttpClient client = new HttpClient();
    List<String> usernames = new List<String>();
    int i = 0;

    foreach (String url in threads)
    {
        i++;
        progressLabel.Text = "Scanning thread " + i.ToString() + "/" + threads.Count<String>();
        HttpResponseMessage response = await client.GetAsync(url);
        String content = await response.Content.ReadAsStringAsync();
        String user;
        Predicate<String> userPredicate;
        foreach (Match match in regex.Matches(content))
        {
            user = match.Groups[1].ToString();
            userPredicate = (String x) => x == user;
            if (usernames.Find(userPredicate) != user)
            {
                usernames.Add(match.Groups[1].ToString());
            }
        }
        progressBar1.PerformStep();
    }
}

我在假设异步和并行处理是相同的情况下对其进行编码,但我刚刚意识到它不是.我查看了我能找到的所有问题,但我似乎真的找不到适合我的例子.他们中的大多数缺乏可读的变量名.使用不解释它们所包含的内容的单字母变量名是陈述示例的可怕方式.

I coded it in the assumption that asynchronous and parallel processing would be the same, and I just realized it isn't. I took a look at all the questions I could find on this, and I really can't seem to find an example that does it for me. Most of them lack readable variable names. Using single-letter variable names which don't explain what they contain is a horrible way to state an example.

我通常在名为线程的数组(包含论坛线程的 URL)中有 300 到 2000 个条目,并且似乎并行处理(由于许多 HTTP 请求)会加快执行速度.

I normally have between 300 and 2000 entries in the array named threads (Contains URL's to forum threads) and it would seem that parallel processing (Due to the many HTTP requests) would speed up the execution).

在我可以使用 Parallel.ForEach 之前,我是否必须删除所有异步(我在 foreach 之外没有任何异步,只有变量定义)?我该怎么做呢?我可以在不阻塞主线程的情况下执行此操作吗?

Do I have to remove all the asynchrony (I got nothing async outside the foreach, only variable definitions) before I can use Parallel.ForEach? How should I go about doing this? Can I do this without blocking the main thread?

顺便说一下,我使用的是 .NET 4.5.

I am using .NET 4.5 by the way.

推荐答案

异步处理和并行处理有很大的不同.如果你不明白其中的区别,我认为你应该先阅读更多相关内容(例如异步和并行编程的关系是什么在 c# 中?).

Asynchronous processing and parallel processing are quite different. If you don't understand the difference, I think you should first read more about it (for example what is the relation between Asynchronous and parallel programming in c#?).

现在,您想要做的实际上并没有那么简单,因为您想要异步处理一个大集合,具有特定的并行度 (8).对于同步处理,您可以使用 Parallel.ForEach()(连同 ParallelOptions 来配置并行度),但是没有简单的替代方法可以与 异步.

Now, what you want to do is actually not that simple, because you want to process a big collection asynchronously, with a specific degree of parallelism (8). With synchronous processing, you could use Parallel.ForEach() (along with ParallelOptions to configure the degree of parallelism), but there is no simple alternative that would work with async.

在您的代码中,由于您希望所有内容都在 UI 线程上执行,因此这很复杂.(虽然理想情况下,您不应该直接从计算中访问 UI.相反,您应该使用 IProgress,这意味着代码不再需要在 UI 线程上执行.)

In your code, this is complicated by the fact that you expect everything to execute on the UI thread. (Though ideally, you shouldn't access the UI directly from your computation. Instead, you should use IProgress, which would mean the code no longer has to execute on the UI thread.)

在 .Net 4.5 中执行此操作的最佳方法可能是使用 TPL Dataflow.它的 ActionBlock 完全符合您的要求,但它可能非常冗长(因为它比您需要的更灵活).所以创建一个辅助方法是有意义的:

Probably the best way to do this in .Net 4.5 is to use TPL Dataflow. Its ActionBlock does exactly what you want, but it can be quite verbose (because it's more flexible than what you need). So it makes sense to create a helper method:

public static Task AsyncParallelForEach<T>(
    IEnumerable<T> source, Func<T, Task> body,
    int maxDegreeOfParallelism = DataflowBlockOptions.Unbounded,
    TaskScheduler scheduler = null)
{
    var options = new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = maxDegreeOfParallelism
    };
    if (scheduler != null)
        options.TaskScheduler = scheduler;

    var block = new ActionBlock<T>(body, options);

    foreach (var item in source)
        block.Post(item);

    block.Complete();
    return block.Completion;
}

在你的情况下,你会像这样使用它:

In your case, you would use it like this:

await AsyncParallelForEach(
    threads, async url => await DownloadUrl(url), 8,
    TaskScheduler.FromCurrentSynchronizationContext());

这里,DownloadUrl() 是处理单个 URL(循环体)的 async Task 方法,8 是并行度(可能不应该是实际代码中的字面常量)和 FromCurrentSynchronizationContext() 确保代码在 UI 线程上执行.

Here, DownloadUrl() is an async Task method that processes a single URL (the body of your loop), 8 is the degree of parallelism (probably shouldn't be a literal constant in real code) and FromCurrentSynchronizationContext() makes sure the code executes on the UI thread.

这篇关于并行运行异步方法 8 次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-30 06:28