开发者

C# WinForms MultiThreading in Loop

Scenario

I have a background worker in my application that runs off and does a bunch of proc开发者_开发百科essing. I specifically used this implementation so as to keep my User Interface fluid and prevent it from freezing up. I want to keep the background worker, but inside that thread, spawn off ONLY 3 MORE threads - making them share the processing (currently the worker thread just loops through and processes each asset one-by-one. However I would like to speed this up but using only a limited number of threads.

Question

Given the code below, how can I get the loop to choose a thread that is free, and then essentially wait if there isn't one free before it continues.

CODE

        foreach (KeyValuePair<int, LiveAsset> kvp in laToHaganise)
        {

             Haganise h = new Haganise(kvp.Value,
                                      busDate,
                                      inputMktSet,
                                      outputMktSet, 
                                      prodType,
                                      noOfAssets, 
                                      bulkSaving);

             h.DoWork();

         }

Thoughts

I'm guessing that I would have to start off by creating 3 new threads, but my concern is that if I'm instantiating a new Haganise object each time - how can I pass the correct "h" object to the correct thread.....

  Thread firstThread = new Thread(new ThreadStart(h.DoWork));
  Thread secondThread =new Thread(new ThreadStart(h.DoWork));
  Thread thirdThread = new Thread(new ThreadStart(h.DoWork));

Help greatly appreciated.


Depending on the size of the job the thread has to do, ThreadPool.QueueUserWorkItem may be a good option. That will create a thread and put it in a queue and execute it when there is thread in the pool available.

Alternatively you could create your own type of Pool which has 3 threads and use ManualResetEvent to determine when a particular thread is finished.


Two possibilities:

  • Use the ThreadPool.QueueUserWorkItem (as already mentioned by James)
  • Implement it BackgroundWorkers on yourself (see below)

Here is a working example, that selects the next free worker:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        private static List<MyWorker> _Workers;

        static void Main(string[] args)
        {
            _Workers = new List<MyWorker>();

            for (int i = 0; i < 5; i++)
            {
                _Workers.Add(CreateDefaultWorker(i));
            }

            StartJobs(20000);
            Console.ReadKey();
        }

        private static void StartJobs(int runtime)
        {
            Random rand = new Random();
            DateTime startTime = DateTime.Now;

            while (DateTime.Now - startTime < TimeSpan.FromMilliseconds(runtime))
            {
                var freeWorker = GetFreeWorker();

                if (freeWorker != null)
                {
                    freeWorker.Worker.RunWorkerAsync(new Action(() => DoSomething(freeWorker.Index, rand.Next(500, 2000))));
                }
                else
                {
                    Console.WriteLine("No free worker available!");
                    Console.WriteLine("Waiting for free one...");
                    WaitForFreeOne();
                }
            }
        }

        private static MyWorker GetFreeWorker()
        {
            foreach (var worker in _Workers)
            {
                if (!worker.Worker.IsBusy)
                    return worker;
            }

            return null;
        }

        private static void WaitForFreeOne()
        {
            while (true)
            {
                foreach (var worker in _Workers)
                {
                    if (!worker.Worker.IsBusy)
                        return;
                }
                Thread.Sleep(1);
            }
        }

        private static MyWorker CreateDefaultWorker(int index)
        {
            var worker = new MyWorker(index);

            worker.Worker.DoWork += (sender, e) => ((Action)e.Argument).Invoke();
            worker.Worker.RunWorkerCompleted += (sender, e) => Console.WriteLine("Job finished in worker " + worker.Index);

            return worker;
        }

        static void DoSomething(int index, int timeout)
        {
            Console.WriteLine("Worker {1} starts to work for {0} ms", timeout, index);
            Thread.Sleep(timeout);
        }
    }

    public class MyWorker
    {
        public int Index { get; private set; }
        public BackgroundWorker Worker { get; private set; }

        public MyWorker(int index)
        {
            Index = index;
            Worker = new BackgroundWorker();
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜