programing

작업을 위해 여러 스레드를 생성 한 다음 모두 완료 될 때까지 기다립니다.

minecode 2021. 1. 17. 10:46
반응형

작업을 위해 여러 스레드를 생성 한 다음 모두 완료 될 때까지 기다립니다.


멀티 스레딩 작업과 관련된 "모범 사례"에 대한 조언을 원합니다.

예를 들어, 시작시 데이터베이스의 다양한 "유형"테이블에서 데이터를 읽고 정보를 컬렉션에 저장하는 C # 응용 프로그램이 있습니다. 이것은 우리가이 정보가 필요할 때마다 데이터베이스에 접근하는 것을 방지합니다.

현재 애플리케이션은 10 개의 테이블에서 동 기적으로 데이터를 읽고 있습니다. 나는 정말로 병렬로 실행되는 다른 스레드의 각 테이블에서 응용 프로그램을 읽도록하고 싶습니다. 애플리케이션은 애플리케이션 시작을 계속하기 전에 모든 스레드가 완료 될 때까지 기다립니다.

BackGroundWorker를 살펴 보았지만 위의 작업을 수행하는 데 몇 가지 조언을 원합니다.

  1. 응용 프로그램의 시작 시간을 단축하기 위해 방법이 논리적으로 들립니까?
  2. 각 스레드의 작업이 서로 독립적이라는 점을 염두에두고 모든 스레드를 어떻게 가장 잘 처리 할 수 ​​있습니까? 계속하기 전에 모든 스레드가 완료 될 때까지 기다려야합니다.

나는 몇 가지 답변을 기대합니다


내가 선호하는 것은 단일 WaitHandle을 통해이를 처리하고 Interlocked를 사용하여 카운터 잠금을 방지하는 것입니다.

class Program
{
    static void Main(string[] args)
    {
        int numThreads = 10;
        ManualResetEvent resetEvent = new ManualResetEvent(false);
        int toProcess = numThreads;

        // Start workers.
        for (int i = 0; i < numThreads; i++)
        {
            new Thread(delegate()
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                // If we're the last thread, signal
                if (Interlocked.Decrement(ref toProcess) == 0)
                    resetEvent.Set();
            }).Start();
        }

        // Wait for workers.
        resetEvent.WaitOne();
        Console.WriteLine("Finished.");
    }
}

이것은 잘 작동하고 잠금을 도입하지 않고 처리하는 스레드 수에 관계없이 확장됩니다.


나는 @Reed의 솔루션을 좋아합니다. .NET 4.0에서 동일한 작업을 수행하는 또 다른 방법은 CountdownEvent 를 사용하는 것 입니다.

class Program
{
    static void Main(string[] args)
    {
        var numThreads = 10;
        var countdownEvent = new CountdownEvent(numThreads);

        // Start workers.
        for (var i = 0; i < numThreads; i++)
        {
            new Thread(delegate()
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                // Signal the CountdownEvent.
                countdownEvent.Signal();
            }).Start();
        }

        // Wait for workers.
        countdownEvent.Wait();
        Console.WriteLine("Finished.");
    }
}

Mark가 말한 것처럼 STA 스레드에 대해 64 개 이상의 대기 핸들이있는 경우. 스레드로 목록을 만들고 두 번째 루프에서 모두 완료 될 때까지 기다릴 수 있습니다.

//check that all threads have completed.
foreach (Thread thread in threadList)
{
     thread.Join();

}  

.NET 4.0이 아닌 경우 각 스레드에 대해 하나씩 List < ManualResetEvent >를 사용하고 Set때까지 기다릴 수 있습니다 . 여러 스레드를 기다리려면 WaitAll 사용을 고려할 수 있지만 64 개의 대기 핸들 제한에주의하십시오. 이보다 더 많이 필요하면 반복해서 하나씩 기다릴 수 있습니다.

더 빠른 시작 경험을 원한다면 시작 중에 모든 데이터를 읽을 때까지 기다릴 필요가 없습니다. GUI 만 표시하면 누락 된 정보는 "업데이트 중 ..."아이콘 또는 이와 유사한 것과 함께 회색으로 표시 될 수 있습니다. 정보가 들어 오면 이벤트를 발생시켜 GUI를 업데이트하십시오. 모든 테이블의 모든 데이터를 읽기 전에 사용자가 수행 할 수있는 많은 작업이있을 수 있습니다.


모험심이 있다면 C # 4.0과 Task Parallel Library를 사용할 수 있습니다.

Parallel.ForEach(jobList, curJob => {
  curJob.Process()
});

다음은 여러 병렬 작업을 기다리는 두 가지 패턴입니다. 트릭은 메인 스레드를 병렬 작업 중 하나 인 것처럼 처리해야한다는 것입니다. 그렇지 않으면 작업자 스레드의 완료 신호와 주 스레드에서 해당 신호를 기다리는 사이에 미묘한 경쟁 조건이 있습니다.

int threadCount = 1;
ManualResetEvent finished = new ManualResetEvent(false);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
  Interlocked.Increment(ref threadCount); 
  ThreadPool.QueueUserWorkItem(delegate 
  { 
      try 
      { 
           // do work 
      } 
      finally 
      { 
          if (Interlocked.Decrement(ref threadCount) == 0) finished.Set();
      } 
  }); 
}
if (Interlocked.Decrement(ref threadCount) == 0) finished.Set();
finished.WaitOne(); 

개인적으로 저는 CountdownEvent 클래스를 사용하여 .NET 4.0에서 사용할 수있는 계산을 수행하는 것을 좋아합니다 .

var finished = new CountdownEvent(1);
for (int i = 0; i < NUM_WORK_ITEMS; i++)
{
  finished.AddCount();
  ThreadPool.QueueUserWorkItem(delegate 
  { 
      try 
      { 
           // do work 
      } 
      finally 
      { 
        finished.Signal();
      } 
  }); 
}
finished.Signal();
finished.Wait(); 

위의 예에서는를 사용 ThreadPool하지만 원하는 스레딩 메커니즘으로 바꿀 수 있습니다.


재미로 @Reed가 Monitor로 한 일입니다. :피

class Program
{
    static void Main(string[] args)
    {
        int numThreads = 10;
        int toProcess = numThreads;
        object syncRoot = new object();

        // Start workers.
        for (int i = 0; i < numThreads; i++)
        {
            new Thread(delegate()
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                // If we're the last thread, signal
                if (Interlocked.Decrement(ref toProcess) == 0)
                {
                    lock (syncRoot)
                    {
                        Monitor.Pulse(syncRoot);
                    }
                }
            }).Start();
        }

        // Wait for workers.
        lock (syncRoot)
        {
            if (toProcess > 0)
            {
                Monitor.Wait(syncRoot);
            }
        }

        Console.WriteLine("Finished.");
    }
}

TPL의 또 다른 가능성 jobs은 처리 할 항목 모음 또는 실행할 하위 스레드 라고 가정 합니다.

Task.WaitAll(jobs
    .Select(job => TaskFactory.StartNew(() => /*run job*/))
    .ToArray());

데이터베이스 판독기 스레드가 완료 되 자마자 반환된다고 가정하면 시작 스레드에서 차례로 모든 10 개의 스레드에 대해 Thread.Join을 호출 할 수 있습니다.


If you are using .NET 3.5 or below, you can use an array of AsyncResult or BackgroundWorker and count how many threads have returned (just don't forget to decrease counter with interlocked operations) (see http://www.albahari.com/threading/ as a reference).

If you are using .NET 4.0 a parallel for is the simplest approach.


an easier method I like to use:

    private int ThreadsCount = 100; //initialize threads count
    private void button1_Click(object sender, EventArgs e)
    {   
        for (int i = 0; i < ThreadsCount; i++)
        {
            Thread t = new Thread(new ThreadStart(myMethod));
            t.IsBackground = true;
            t.Start(); 
        } 
    }

    private void myMethod()
    {
        //everytime a thread finishes executing decrease the threads count
        ThreadsCount = ThreadsCount - 1;

        if (ThreadsCount < 1)
        {
            //if all threads finished executing do whatever you wanna do here..
            MessageBox.Show("Finished Executing all threads!!!");
        }
    }

Posting to maybe help some others, spent quite a bit of time looking for a solution like what I came up with. So I took a little different approach. I was spinning off numerous threads and incremented a counter and decremented a counter as a thread started and stopped. Then in the main method I was wanting to pause and wait for threads to complete I did.

while (threadCounter > 0)
{
    Thread.Sleep(500); //Make it pause for half second so that we don’t spin the cpu out of control.
}

Documented on my blog. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

ReferenceURL : https://stackoverflow.com/questions/2528907/spawn-multiple-threads-for-work-then-wait-until-all-finished

반응형