Каков наилучший способ, чтобы несколько потоков работали и ожидали завершения всех из них?

 $jmldata     = $db->query("SELECT count(*) FROM blog_posts")->fetchColumn();
  $jmlhalaman  = $p->jumlahHalaman($jmldata, $batas);
  $linkHalaman = $p->navHalaman($_GET['halpost'], $jmlhalaman);

  echo "<div class='center_title_bar'>Halaman : $linkHalaman </div>
  </div></div></div>
  ";

он работает благодаря:)

13
задан BFree 17 December 2009 в 04:59
поделиться

8 ответов

Вы по-прежнему хотите использовать ThreadPool, потому что он будет управлять количеством потоков, выполняемых одновременно. Недавно я столкнулся с подобной проблемой и решил ее следующим образом:

var dispatcher = new ThreadPoolDispatcher();
dispatcher = new ChunkingDispatcher(dispatcher, 10);

foreach (var image in images)
{
    dispatcher.Add(new ResizeJob(image));
}

dispatcher.WaitForJobsToFinish();

IDispatcher и IJob выглядят следующим образом:

public interface IJob
{
    void Execute();
}

public class ThreadPoolDispatcher : IDispatcher
{
    private IList<ManualResetEvent> resetEvents = new List<ManualResetEvent>();

    public void Dispatch(IJob job)
    {
        var resetEvent = CreateAndTrackResetEvent();
        var worker = new ThreadPoolWorker(job, resetEvent);
        ThreadPool.QueueUserWorkItem(new WaitCallback(worker.ThreadPoolCallback));
    }

    private ManualResetEvent CreateAndTrackResetEvent()
    {
        var resetEvent = new ManualResetEvent(false);
        resetEvents.Add(resetEvent);
        return resetEvent;
    }

    public void WaitForJobsToFinish()
    {
        WaitHandle.WaitAll(resetEvents.ToArray() ?? new ManualResetEvent[] { });
        resetEvents.Clear();
    }
}

А затем использовал декоратор для фрагментации использования ThreadPool:

public class ChunkingDispatcher : IDispatcher
{
    private IDispatcher dispatcher;
    private int numberOfJobsDispatched;
    private int chunkSize;

    public ChunkingDispatcher(IDispatcher dispatcher, int chunkSize)
    {
        this.dispatcher = dispatcher;
        this.chunkSize = chunkSize;
    }

    public void Dispatch(IJob job)
    {
        dispatcher.Dispatch(job);

        if (++numberOfJobsDispatched % chunkSize == 0)
            WaitForJobsToFinish();
    }

    public void WaitForJobsToFinish()
    {
        dispatcher.WaitForJobsToFinish();
    }
}

Абстракция IDispatcher довольно хорошо работает для обмена из вашей техники резьбы. У меня есть еще одна реализация - SingleThreadedDispatcher, и вы можете создать версию ThreadStart, как предложил Джон Скит. Затем это' Легко запустить каждый из них и посмотреть, какую производительность вы получите. SingleThreadedDispatcher хорош при отладке кода или когда вы не хотите убивать процессор на вашем компьютере.

Изменить: Я забыл добавить код для ThreadPoolWorker:

public class ThreadPoolWorker
{
    private IJob job;
    private ManualResetEvent doneEvent;

    public ThreadPoolWorker(IJob job, ManualResetEvent doneEvent)
    {
        this.job = job;
        this.doneEvent = doneEvent;
    }

    public void ThreadPoolCallback(object state)
    {
        try
        {
            job.Execute();
        }
        finally
        {
            doneEvent.Set();
        }
    }
}
11
ответ дан 1 December 2019 в 19:31
поделиться

Самым простым было бы создать новые потоки, а затем вызвать Thread.Join для каждого из них. Вы могли бы использовать семафор или что-то в этом роде, но, вероятно, проще просто создать новые потоки.

В .NET 4.0 вы можете использовать Parallel Extensions, чтобы сделать это довольно легко с задачами.

Как другая альтернатива, которая будет использовать пул потоков, вы можете создать делегат и вызвать для него BeginInvoke , чтобы вернуть IAsyncResult - тогда вы можете получить WaitHandle для каждого результата через свойство AsyncWaitHandle и вызов WaitHandle.WaitAll .

РЕДАКТИРОВАТЬ: Как указано в комментариях, вы можете только вызвать WaitAll с 64 дескрипторами одновременно в некоторых реализациях. Альтернативой может быть вызов WaitOne для каждого из них по очереди или вызов WaitAll с использованием пакетов. На самом деле это не имеет значения, если вы делаете это из потока, который не будет блокировать пул потоков. Также обратите внимание, что вы не можете вызвать WaitAll из потока STA.

11
ответ дан 1 December 2019 в 19:31
поделиться

Самым простым и эффективным решением было бы использовать счетчики и сделать их потокобезопасными. Это потребляет меньше памяти и может масштабироваться до большего числа потоков

Вот пример

int itemCount = 0;
for (int i = 0; i < 5000; i++)
{
    Interlocked.Increment(ref itemCount);

    ThreadPool.QueueUserWorkItem(x=>{
        try
        {
            //code logic here.. sleep is just for demo
            Thread.Sleep(100);
        }
        finally
        {
            Interlocked.Decrement(ref itemCount);
        }
    });
}

while (itemCount > 0)
{
    Console.WriteLine("Waiting for " + itemCount + " threads...");
    Thread.Sleep(100);
}
Console.WriteLine("All Done!");
5
ответ дан 1 December 2019 в 19:31
поделиться

Я использовал SmartThreadPool с большим количеством удается справиться с этой проблемой. Существует также сайт Codeplex , посвященный сборке.

SmartThreadPool может помочь с другими проблемами, например, некоторые потоки не могут работать одновременно, а другие могут.

2
ответ дан 1 December 2019 в 19:31
поделиться

Я использую статический служебный метод для проверки всех отдельных дескрипторов ожидания.

    public static void WaitAll(WaitHandle[] handles)
    {
        if (handles == null)
            throw new ArgumentNullException("handles",
                "WaitHandle[] handles was null");
        foreach (WaitHandle wh in handles) wh.WaitOne();
    }

Затем в основном потоке я создаю список этих дескрипторов ожидания, и для каждого делегата, помещаемого в мою очередь ThreadPool, я добавляю дескриптор ожидания в Список ...

 List<WaitHandle> waitHndls = new List<WaitHandle>();
 foreach (iterator logic )
 {
      ManualResetEvent txEvnt = new ManualResetEvent(false);

      ThreadPool.QueueUserWorkItem(
           delegate
               {
                   try { // Code to process each task... }
                   // Finally, set each wait handle when done
                   finally { lock (locker) txEvnt.Set(); } 
               });
      waitHndls.Add(txEvnt);  // Add wait handle to List
 }
 util.WaitAll(waitHndls.ToArray());   // Check all wait Handles in List
2
ответ дан 1 December 2019 в 19:31
поделиться

Другой вариант - использовать канал.

Вы отправляете всю работу, которая должна быть сделана, в конвейер, а затем считываете данные из конвейера из каждого потока. Когда канал пуст, все готово, потоки заканчиваются сами по себе, и все довольны (конечно, убедитесь, что вы сначала выполнили всю работу, а затем использовали ее)

1
ответ дан 1 December 2019 в 19:31
поделиться

I suggest putting the untouched images in a queue and as you read from the queue launch a thread and insert its System.Threading.Thread.ManagedThreadId property into a dictionary along with the file name. This way your UI can list both pending and active files.

When each thread completes it invokes a callback routine, passing back its ManagedThreadId. This callback (passed as a delegate to the thread) removes the thread's id from the dictionary, launches another thread from the queue, and updates the UI.

When both the queue and the dictionary are empty, you're done.

Slightly more complicated but this way you get a responsive UI, you can easily control the number of active threads, and you can see what's in flight. Collect statistics. Get fancy with WPF and put up progress bars for each file. She can't help but be impressed.

1
ответ дан 1 December 2019 в 19:31
поделиться

.Net 4.0 делает многопоточность еще проще (хотя вы все равно можете столкнуться с побочными эффектами).

2
ответ дан 1 December 2019 в 19:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: