gpt4 book ai didi

c# - 如何在C#中使用多线程进行批处理

转载 作者:行者123 更新时间:2023-12-03 13:16:36 29 4
gpt4 key购买 nike

我正在使用并行foreach在阻止集合中添加值,但是当阻止集合具有10个值时,我需要对此进行一些处理,然后再次清除该阻止集合,然后再次开始向该阻止集合添加值。
这里有两个问题

  • 在我进行一些处理时,它将继续向阻塞集合中添加值,我可以在列表上放置一个锁,但是直到它到达该锁时,该值就会增加。
  • 如果我要放的锁完全破坏了并行编程的使用,我希望在该列表中进行该对象的添加,直到处理这10条消息为止。我可以复制列表内容并在此处再次清空列表,同样的问题,我不能仅复制10个项目,因为内容已经更改。

  • 有时,如果条件永远不会满足,因为在检查条件之前,值会增加。
    有什么解决办法吗?
    public static BlockingCollection<string> failedMessages = new BlockingCollection<string>();
    static void Main(string[] args)
    {
    var myCollection = new List<string>();
    myCollection.Add("test");
    //Consider myCollection having more than 100 items
    Parallel.ForEach(myCollection, item =>
    {
    failedMessages.Add(item);
    if (failedMessages.Count == 10)
    {
    DoSomething();
    }
    });

    }

    static public void DoSomething()
    {
    //dosome operation with failedMessages
    failedMessages = new BlockingCollection<string>();
    }


    最佳答案

    这看起来像是DataFlow的工作:
    使用批处理大小为10的BatchBlock<string>和使用ActionBlock<string[]>来使用Batchs的示例:

    using System;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;

    public class Program
    {
    public static void Main()
    {
    Console.WriteLine("Hello World");
    // Set up DataFlow Blocks
    BatchBlock<string> batcher = new BatchBlock<string>( 10 );
    ActionBlock<string[]> consumer =
    new ActionBlock<string[]>(
    (msgs) => Console.WriteLine("Processed {0} messages.", msgs.Length)
    );
    // put them together
    batcher.LinkTo( consumer );

    // start posting
    Parallel.For( 0, 103, (i) => batcher.Post(string.Format("Test {0}",i)));

    // shutdown
    batcher.Complete();
    batcher.Completion.Wait();
    }
    }
    实际应用中: https://dotnetfiddle.net/Y9Ezg4
    扩展阅读: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/walkthrough-using-batchblock-and-batchedjoinblock-to-improve-efficiency

    编辑:根据要求-如果您不能或不想使用DataFlow,您当然可以做类似的事情:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    using System.Linq;

    public class Program
    {
    public static void Main()
    {
    FailedMessageHandler fmh = new FailedMessageHandler( new Progress<string[]>((list) => { Console.WriteLine("Handling {0} messages. [{1}]", list.Length, string.Join(",", list));}));
    Parallel.For(0,52, (i) => {fmh.Add(string.Format("Test {0,3}",i));});
    Thread.Sleep(1500); // Demo: Timeout
    var result = Parallel.For(53,107, (i) => {fmh.Add(string.Format("Test {0,3}",i));});
    while(!result.IsCompleted)
    {
    // Let Parallel.For run to end ...
    Thread.Sleep(10);
    }
    // Graceful shutdown:
    fmh.CompleteAdding();
    fmh.AwaitCompletion();
    }
    }

    public class FailedMessageHandler
    {
    private BlockingCollection<string> workQueue = new BlockingCollection<string>();
    private List<string> currentBuffer = new List<string>(10);
    private IProgress<string[]> progress;
    private Thread workThread;

    public FailedMessageHandler( IProgress<string[]> progress )
    {
    this.progress = progress;
    workThread = new Thread(WatchDog);
    workThread.Start();
    }

    public void Add( string failedMessage )
    {
    if ( workQueue.IsAddingCompleted )
    {
    throw new InvalidOperationException("Adding is completed!");
    }

    workQueue.Add(failedMessage);
    }

    private void WatchDog()
    {
    while(true)
    {
    // Demo: Include a timeout - If there are less than 10 items
    // for x amount of time, send whatever you got so far.
    CancellationTokenSource timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
    try{
    var failedMsg = workQueue.Take(timeout.Token);
    currentBuffer.Add(failedMsg);
    if( currentBuffer.Count >= 10 ){
    progress.Report(currentBuffer.ToArray());
    currentBuffer.Clear();
    }
    }
    catch(OperationCanceledException)
    {
    Console.WriteLine("TIMEOUT!");
    // timeout.
    if( currentBuffer.Any() ) // handle items if there are
    {
    progress.Report(currentBuffer.ToArray());
    currentBuffer.Clear();
    }
    }
    catch(InvalidOperationException)
    {
    Console.WriteLine("COMPLETED!");
    // queue has been completed.
    if( currentBuffer.Any() ) // handle remaining items
    {
    progress.Report(currentBuffer.ToArray());
    currentBuffer.Clear();
    }
    break;
    }
    }
    Console.WriteLine("DONE!");
    }

    public void CompleteAdding()
    {
    workQueue.CompleteAdding();
    }

    public void AwaitCompletion()
    {
    if( workThread != null )
    workThread.Join();
    }
    }
    实际中: https://dotnetfiddle.net/H2Rg35
    请注意,使用 Progress将在主线程上执行处理。如果您改为传递 Action,它将在 workThread上执行。因此,请根据您的要求调整示例。
    这也只是一个想法,它有很多变化,也许使用Task/Async ...

    关于c# - 如何在C#中使用多线程进行批处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62715808/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com