我如何重构这个ForEach(.)代码来使用Parallel.ForEach(.) ?

本文关键字:ForEach 代码 Parallel 何重构 重构 | 更新日期: 2023-09-27 17:50:26

我有一个对象列表,我希望从一个源复制到另一个。有人建议我可以使用Parallel来加快速度。ForEach

如何重构以下伪代码以利用Parallel.ForEach(..) ?

var foos = GetFoos().ToList();
foreach(var foo in foos)
{
    CopyObjectFromOldBucketToNewBucket(foo, oldBucket, newBucket, 
        accessKeyId, secretAccessKey);
}

CopyObjectFromOldBucketToNewBucket使用Amazon REST api将项从一个桶移动到另一个桶。

欢呼:)

我如何重构这个ForEach(.)代码来使用Parallel.ForEach(.) ?

Parallel实际上不是这里的最佳选择。Parallel将并行运行您的代码,但是对于每个到AWS的请求,仍然会使用一个线程池线程。使用BeginCopyObject方法会更好地利用资源。这将不会耗尽等待响应的线程池,而只会在接收到响应并需要处理时使用它。

下面是如何使用Begin/End方法的一个简化示例。这些不是特定于AWS的,而是贯穿于。net BCL的模式。
public static CopyFoos() 
{
    var client = new AmazonS3Client(...);
    var foos = GetFoos().ToList();
    var asyncs = new List<IAsyncResult>();
    foreach(var foo in foos)
    {
        var request = new CopyObjectRequest { ... };  
        asyncs.Add(client.BeginCopyObject(request, EndCopy, client));
    }
    foreach(IAsyncResult ar in asyncs)
    {
        if (!ar.IsCompleted)
        {
            ar.AsyncWaitHandle.WaitOne();
        }
    }
}
private static EndCopy(IAsyncRequest ar) 
{    
    ((AmazonS3Client)ar.AsyncState).EndCopyObject(ar);
}

对于生产代码,你可能想要跟踪你已经发送了多少请求,并且每次只发送有限数量的请求。测试或AWS文档可能会告诉您并发请求的数量是最佳的。

在这种情况下,当请求完成时,我们实际上不需要做任何事情,所以您可能会跳过EndCopy调用,但这会导致资源泄漏。当你调用BeginXxx时,你必须调用相应的EndXxx方法

由于您的代码除了foos之外没有任何依赖关系,您可以简单地执行:

Parallel.ForEach(foos, ( foo => 
{
    CopyObjectFromOldBucketToNewBucket(foo, oldBucket, newBucket, 
                                       accessKeyId, secretAccessKey);
}));

请记住,I/O只能并行到一定程度,否则性能可能会下降。