如何使所有线程完成工作,直到从C#中的线程池开始工作

时间:2019-12-16 09:35:29

标签: c# multithreading threadpool

for (int task = 0; task < 20; task++)
{

    ThreadPool.QueueUserWorkItem(new WaitCallback(TaskCallBack), new object[] { filepath1, filepath2, 
                  filepath3 });

}

public static void TaskCallBack(object state)
{
            object[] array = state as object[];
            string filea = Convert.ToString(array[0]);
            string fileb = Convert.ToString(array[1]);
            string filec = Convert.ToString(array[2]);
            //something below         

}

我希望等待主线程,直到所有线程完成其工作。请帮助

2 个答案:

答案 0 :(得分:2)

处理此问题的最佳方法是使用Task.Run()Task.WhenAll(),或使用Parallel.Invoke()

但是,如果您需要使用ThreadPool.QueueUserWorkItem,则可以按以下方法解决此问题:

  1. 为便于使用,请将要传递给线程的所有数据封装在一个类中。该数据应包括CountdownEvent实例,该实例的初始化次数等于您要等待的线程数。 (在下面的示例代码中,此类称为ThreadData。)
  2. 在您的TaskCallBack()方法内,方法完成后调用CountdownEvent.Signal()
  3. 在主线程中,启动所有线程池线程,然后调用CountdownEvent.Wait()以等待所有线程完成。

将所有内容放到可编译的控制台应用中:

using System;
using System.Threading;

namespace CoreConsole
{
    public sealed class ThreadData
    {
        public ThreadData(CountdownEvent countdown, int index)
        {
            Countdown = countdown;
            Index     = index;
        }

        public CountdownEvent Countdown { get; }
        public int Index { get; }
    }

    public static class Program
    {
        static void Main()
        {
            int n = 20;
            var countdown  = new CountdownEvent(n);

            for (int task = 0; task < n; task++)
            {
                ThreadPool.QueueUserWorkItem(TaskCallBack, new ThreadData(countdown, task));
            }

            Console.WriteLine("Waiting for all threads to exit");

            countdown.Wait();

            Console.WriteLine("Waited for all threads to exit");
        }

        public static void TaskCallBack(object state)
        {
            var data = (ThreadData) state;

            Console.WriteLine($"Thread {data.Index} is starting.");

            Thread.Sleep(_rng.Next(2000, 10000));
            data.Countdown.Signal();

            Console.WriteLine($"Thread {data.Index} has finished.");
        }

        static readonly Random _rng = new Random(45698);
    }
}

ThreadData.Index属性仅用于标识Console.WriteLine()调用中的每个线程。

注意:在实际代码中,即使线程引发异常,始终发出Countdown事件也很重要-因此,您应该像这样将代码包装在try / final中:

public static void TaskCallBack(object state)
{
    var data = (ThreadData)state;

    try
    {
        // Do work here.
        Console.WriteLine($"Thread {data.Index} is starting.");
        Thread.Sleep(_rng.Next(2000, 10000));
        Console.WriteLine($"Thread {data.Index} has finished.");
    }

    finally
    {
        data.Countdown.Signal();
    }
}

答案 1 :(得分:1)

就像@Ackdari在他的评论中提到的那样,您可以使用 Task.Run 。但是您不需要 await 关键字。只需创建包含任务的集合,然后等待即可。

示例:

// Create a list that will hold the tasks
List<Task> tasks = new List<Task>;

// Create the tasks
for (int taskId = 0; taskId < 20; task++)
{
  tasks.Add(Task.Run(() => { TaskCallBack(new object[] { filepath1, filepath2, filepath3 }); }));
}

// Wait for ALL tasks to complete
Task.WaitAll(tasks.ToArray());

这样,您还可以使用自己的方法,该方法将由任务运行。 示例:

public static void ReplaceWithABetterName(string[] filePaths)
{
   string filea = filePaths[0);
   string fileb = filePaths[1];
   string filec = filePaths[2];
   //do stuff       
}
相关问题