我有一個 WPF 應用程式,它讀取 Outlook .pst 檔案,提取每封郵件,并將它和任何附件保存為 .pdf 檔案。全部完成后,它會對檔案進行一些其他處理。
我目前foreach在第一部分使用一個普通的舊回圈。這是代碼的一個相當簡化的版本......
// These two are used by the WPF UI to display progress
string BusyContent;
ObservableCollection<string> Msgs = new();
// See note lower down about the quick-and-dirty logging
string _logFile = @"C:\Path\To\LogFile.log";
// _allFiles is used to keep a record of all the files we generate. Used after the loop ends
List<string> _allFiles = new();
// nCurr is used to update BusyContent, which is bound to the UI to show progress
int nCurr = 0;
// The messages would really be extracted from the .pst file. Empty list used for simplicity
List<Message> messages = new();
async Task ProcessMessages() {
using StreamWriter logFile = new(_logFile, true);
foreach (Message msg in messages) {
nCurr ;
string fileName = GenerateFileName(msg);
// We log a lot more, but only one shown for simplicity
Log(logFile, $"File: {fileName}");
_allFiles.Add(fileName);
// Let the user know where we are up to
BusyContent = $"Processing message {nCurr}";
// Msgs is bound to a WPF grid, so we need to use Dispatcher to update
Application.Current.Dispatcher.Invoke(() => Msgs.Add(fileName));
// Finally we write out the .pdf files
await ProcessMessage(msg);
}
}
async Task ProcessMessage(Message msg) {
// The methods called here are omitted as they aren't relevant to my questions
await GenerateMessagePdf(msg);
foreach(Attachment a in msg.Attachments) {
string fileName = GenerateFileName(a);
// Note that we update _allFiles here as well as in the main loop
_allFiles.Add(fileName);
await GenerateAttachmentPdf(a);
}
}
static void Log(StreamWriter logFile, string msg) =>
logFile.WriteLine(DateTime.Now.ToString("yyMMdd-HHmmss.fff") " - " msg);
這一切正常,但在大型 .pst 檔案上可能需要相當長的時間。我想知道將它轉換為 useParallel.ForEach是否會加快速度。我可以看到這個方法的基本用法,但是有幾個問題,主要是關于回圈內使用的類級變數......
該
logFile變數被傳來傳去。這會導致問題嗎?這不是一個大問題,因為這個日志記錄是作為一個快速而骯臟的除錯設備添加的,真的應該用適當的日志記錄框架替換,但我仍然想知道我是什么東東并行版本中的一個問題nCurr在回圈內更新。這是安全的,還是有更好的方法來做到這一點?_allFiles也在主回圈內更新。我只是添加條目,而不是閱讀或洗掉,但這安全嗎?同樣,
_allFiles在ProcessMessage方法內部進行更新。我想這個問題的答案取決于前一個。更新 BusyContent 并在回圈內呼叫 Application.Current.Dispatcher.Invoke 是否有問題?
謝謝你提供的所有幫助。
uj5u.com熱心網友回復:
首先,需要使用執行緒安全的集合:
ObservableConcurrentCollection<string> Msgs = new();
ConcurrentQueue<string> _allFiles = new();
ObservableConcurrentCollection可以通過 NuGet安裝。ConcurrentQueue位于using System.Collections.Concurrent;. 特別感謝西奧多Zoulias為指出有更好的選擇ConcurentBag。
然后就可以使用Parallel.ForEachor 了Task。
Parallel.ForEach使用Partitioner 它可以避免創建不必要的任務。所以它嘗試并行運行每個方法。所以最好排除 參與的方法的關鍵字async和await關鍵字Parallel.ForEach。
async Task ProcessMessages()
{
using StreamWriter logFile = new(_logFile, true);
await Task.Run(() => {
Parallel.ForEach(messages, msg =>
{
var currentCount = Interlocked.Increment(ref nCurr);
string fileName = GenerateFileName(msg);
Log(logFile, $"File: {fileName}");
_allFiles.Enqueue(fileName);
BusyContent = $"Processing message {currentCount}";
ProcessMessage(msg);
});
});
}
int ProcessMessage(Message msg)
{
// The methods called here are omitted as they aren't relevant to my questions
var message = GenerateMessagePdf(msg);
foreach (Attachment a in msg.Attachments)
{
string fileName = GenerateFileName(a);
_allFiles.Enqueue(fileName);
GenerateAttachmentPdf(a);
}
return msg.Id;
}
private string GenerateAttachmentPdf(Attachment a) => string.Empty;
private string GenerateMessagePdf(Message message) => string.Empty;
string GenerateFileName(Attachment attachment) => string.Empty;
string GenerateFileName(Message message) => string.Empty;
void Log(StreamWriter logFile, string msg) =>
logFile.WriteLine(DateTime.Now.ToString("yyMMdd-HHmmss.fff") " - " msg);
另一種方法是等待所有任務。在這種情況下,不需要排除async和await關鍵字。
async Task ProcessMessages()
{
using StreamWriter logFile = new(_logFile, true);
var messageTasks = messages.Select(msg =>
{
var currentCount = Interlocked.Increment(ref nCurr);
string fileName = GenerateFileName(msg);
Log(logFile, $"File: {fileName}");
_allFiles.Enqueue(fileName);
BusyContent = $"Processing message {currentCount}";
return ProcessMessage(msg);
});
var msgs = await Task.WhenAll(messageTasks);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/346711.html
標籤:C# 异步 线程安全 任务并行库 parallel.foreach
