[英]Net 6 ConsoleApp multiple BlockingCollection<T> huge CPU consumption
我有一個 Net 6 控制台應用程序,我在其中使用多個 BlockingCollections 來處理放在文件夾中的文件。 我使用 Net 的FileWatcher()
觀看文件夾。
在 Created 事件中,我使用一個Channel
來處理處理,它分兩個階段完成,每個階段之后結果項被移動到 BlockingCollection,然后將被下一階段使用。
程序.cs
public static async Task Main(string[] args)
{
BlockingCollection<FileMetadata> _fileMetaDataQueue = new BlockingCollection<FileMetadata>()
var channel = Channel.CreateUnbounded<FileSystemEventArgs>();
// Start a task to monitor the channel and process notifications
var notificationProcessor = Task.Run(() => ProcessNotifications(channel, _fileMetaDataQueue));
Task fileCopyingTask = Task.Run(() => fileCopyThread.Start()); //injected using DI
Task processMovedFile = Task.Run(() => ProcessDestinationThread.Start()); //injected using DI
Task retryOnErrorTask = Task.Run(() => RetryOnErrorThread.Start()); //injected using DI
using var watcher = new FileSystemWatcher(sourceFolder); //C:\temp
// other fw related config
watcher.Created += (sender, e) => channel.Writer.WriteAsync(e);
}
private async Task ProcessNotifications(Channel<FileSystemEventArgs> channel, BlockingCollection<FileMetadata> queue)
{
await foreach (var e in channel.Reader.ReadAllAsync())
{
Thread.Sleep(300); // So the file is released after it is dropped
try
{
// Process the file and add its name and extension to the queue
FileMetaData fileInfo = ExtractFileMetadata(e.FullPath); //processing method
queue.Add(fileInfo);
}
try
{
// logging etc
}
}
}
然后在 FileCopyThread class 中使用 BlockingCollection queue
,並公開(並調用)Start() 方法
文件復制線程.cs
BlockingCollection<FileMetadata> resultQueue = new();
BlockingCollection<FileMetadata> retryQueue = new();
public async Task Start()
{
await Task.Run(() => {
ProcessQueue();
});
}
private void ProcessQueue()
{
// Since IsCompleted is never set, it will always run
while (!fileMetadataQueue.IsCompleted)
{
// Try to remove an item from the queue
if (fileMetadataQueue.TryTake(out FileMetadata result))
{
// Copy the file to a new location
var newFileLocation = processorOps.MoveFile(result); // move file to other path
// Add the new file location to the result queue
if (newFileLocation != String.Empty)
{
result.newFileLocation = newFileLocation;
resultQueue.Add(result);
}
else {
retryQueue.Add(result);
}
}
}
}
ProcessDestinationThread
和RetryOnErrorThread
的工作方式完全相同,但處理方式不同,分別使用resultQueue
和retryQueue
。 現在,當我運行這個應用程序時,它工作正常,一切都按預期進行處理,但我的 CPU 和電源使用率在 85% 到 95% 之間,這是巨大的,IMO,即使它沒有處理任何東西,只是坐着閑。 我想這是因為所有的無限循環,但我該如何補救呢? 鳥瞰圖:我想要的是,如果filewatcher.created
事件沒有觸發(即沒有文件被丟棄),那么它之后的所有隊列都可以空閑運行,可以這么說。 那么就不需要經常檢查了。
我想過在BlockingCollection<T>
上調用CompleteAdding()
,但我似乎無法逆轉它。 該應用程序應該無限期運行:因此,如果放置文件夾為空,它可能隨時接收新文件。
有什么方法可以降低我的應用程序的 CPU 使用率?
附言。 我知道這段代碼不是一個完整的示例。 真正的代碼比這復雜得多,我不得不刪除很多分散注意力的東西。 如果您認為缺少任何相關代碼,我可以提供。 我希望這段代碼至少能弄清楚我想要實現的目標。
private void ProcessQueue() { while (.fileMetadataQueue.IsCompleted) { if (fileMetadataQueue.TryTake(out FileMetadata result)) { //... } } }
這種使用BlockingCollection<T>
的模式是不正確的。 它會導致一個緊密循環,無效率地消耗 CPU 內核。 正確的模式是使用GetConsumingEnumerable
方法:
private void ProcessQueue()
{
foreach (FileMetadata result in fileMetadataQueue.GetConsumingEnumerable())
{
//...
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.