簡體   English   中英

Net 6 ConsoleApp 多個 BlockingCollection<t> 巨大的CPU消耗</t>

[英]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);                        
           }
        }
    }
}

ProcessDestinationThreadRetryOnErrorThread的工作方式完全相同,但處理方式不同,分別使用resultQueueretryQueue 現在,當我運行這個應用程序時,它工作正常,一切都按預期進行處理,但我的 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM