Solved

Threads Syncronization in a ThreadPool

Posted on 2004-05-01
22
428 Views
Last Modified: 2008-02-01
I am trying to syncronize some threads so they wait for the current step to be done by every thread before executing the next step. I am using the standard thread pool for the workers and the main thread for syncronization. Everything works fine, but the first WaitHandle.WaitAll() call has a delay that I can't figure out. Can somebody tell me how to bypass that delay?

Here you have the code:


using System;
using System.Threading;

namespace SincronizareThreaduri
{
      class ThreadsTest
      {
            int nrPasi = 3;
            int nrProcesoare = 5;
            ManualResetEvent[] manualEventsFromThreads;
            ManualResetEvent[] manualEventsToThreads;

            static void Main(string[] args)
            {
                  ThreadsTest c = new ThreadsTest();
            }

            public ThreadsTest()
            {
                  manualEventsFromThreads = new ManualResetEvent[nrProcesoare];
                  manualEventsToThreads = new ManualResetEvent[nrProcesoare];

                  for(int i=0; i<nrProcesoare; i++)
                  {
                        manualEventsFromThreads[i] = new ManualResetEvent(false);
                        manualEventsToThreads[i] = new ManualResetEvent(false);
                        
                        Procesor p = new Procesor(i, nrPasi, manualEventsFromThreads[i],
                              manualEventsToThreads[i]);

                        ThreadPool.QueueUserWorkItem(new WaitCallback(p.Work));
                  }

                  for(int pasCurent = 0; pasCurent<nrPasi; pasCurent++)
                  {
                        int tick = Environment.TickCount;
                        WaitHandle.WaitAll(manualEventsFromThreads);
                        Console.WriteLine("pas " + pasCurent + ", wait: " + (Environment.TickCount - tick));
                        
                        for(int i=0; i<nrProcesoare; i++)
                        {
                              manualEventsFromThreads[i].Reset();
                              manualEventsToThreads[i].Set();
                        }
                  }
            }
      }

      class Procesor
      {
            int procesorCurent;
            int pasCurent = 0;
            int nrPasi;
            ManualResetEvent manualEventToMain;
            ManualResetEvent manualEventFromMain;

            public Procesor(int procID, int nrPasi, ManualResetEvent to, ManualResetEvent from)      
            {
                  procesorCurent = procID;
                  this.nrPasi = nrPasi;
                  manualEventToMain = to;
                  manualEventFromMain = from;
            }

            public void Work(object state)
            {
                  while(pasCurent < nrPasi)
                  {
                        Console.WriteLine("Procesor " + procesorCurent + " pasul " + pasCurent);
                        
                        manualEventToMain.Set();
                        manualEventFromMain.WaitOne();
                        manualEventFromMain.Reset();

                        pasCurent++;
                  }
            }
      }
}
0
Comment
Question by:fulgeru99
  • 8
  • 7
  • 5
22 Comments
 
LVL 4

Expert Comment

by:AndreSteffens
ID: 10968703
After doing some testing the only explanation I can come up with is:
Initialising the Work method takes 500 ticks. Here the Processor is being assigned its thread from the pool. After that the while loop takes over and the thread has to wait from time to time of course, but there is no new initialisation involved, gaining a lot of time!
Strangely enough, when I put in another for loop, encircling the initialisation and threadpoolqueueingm the wait still remains 0!
0
 

Author Comment

by:fulgeru99
ID: 10972376
Can you come with a solution?
I had another solution before, but it wasn't eficient. I was instatiating the Thread class. And starting it with Thread.Start(). When I had to syncronize the threads I was suspending them, and inn the main thread I had a while loop that was checking the state ot each thread. When all of them where in the Suspended state the step was over, so the main thread was resuming them and go to the next step.
Now, I've found this solution with ThreadPool and comunication with signals between the threads, but I get this stupid timeout.
0
 
LVL 4

Expert Comment

by:AndreSteffens
ID: 10973326
There are other ways to tackle this of course, though I found your way rather neat! Perhaps I will have some time to do experiments tomorrow evening (i.e 20 hours from now). Multithreading gives me always the same bewildering en fascinating experience as watching a good star trek show involving time travel (Yesterday's Enterpise comes to mind ;-))
Are you sure you can't live with an initial delay of 500 ticks per thread on the very first pass of your program only?
0
 

Author Comment

by:fulgeru99
ID: 10984292
I have solved the problem using Thread class instances, but I want to know how to do it with a ThreadPool too.
0
 
LVL 10

Expert Comment

by:ptmcomp
ID: 10990960
What excatly should the code do? I think there is a different way to achieve it. I think the synchronization overhead is much too big. Your almost telling the system how to schedule the time slices for the threads.
0
 

Author Comment

by:fulgeru99
ID: 10992059
This code should run in no time. It must not have that stupid delay.
0
 
LVL 10

Expert Comment

by:ptmcomp
ID: 10997613
I still didn't have time to test it. Anyway I would use Thread class instances and Thread.Join(..)
0
 
LVL 10

Expert Comment

by:ptmcomp
ID: 10999776
I think the threadpool is smaller and grows delayed. If you test this:
         static void Main(string[] args)
          {
               Console.WriteLine("---Start:---");
               ThreadsTest c = new ThreadsTest();
               Console.WriteLine("---Start:---");
               c = new ThreadsTest();
               Console.WriteLine("---Start:---");
               c = new ThreadsTest();
          }
The delay is only the first time.
0
 
LVL 10

Expert Comment

by:ptmcomp
ID: 10999819
0
 

Author Comment

by:fulgeru99
ID: 10999928
Using the Thread class an ManualResetEvent signals i was able to do it before posting this topic, but I wanna know how to do it eficiently within a TgreadPool
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 10

Expert Comment

by:ptmcomp
ID: 11000131
The problem is the QueueUserWorkItem after startup of the project. There are no workerhtreads available and the system waits 500ms (may be hardcoded timespan) till it allocates one. Enter in Goolge: "ThreadPool 500ms" and see what you get...
0
 
LVL 4

Expert Comment

by:AndreSteffens
ID: 11021580
The code below will eliminate the firstpass wait that annoys you so much AND uses the ThreadPool.
It uses the BeginInvoke method of a delegate, which will cause the method that the delegate points to to execute aynchronously using the threadpool. Through the delegate's asyncresult a waithandle.waitall is obtained.
I think this is the anwer you were looking for...

namespace ConsoleApplication1
{
      using System;
      using System.Threading;
      using System.Collections;

      namespace SincronizareThreaduri
      {
            class ThreadsTest
            {
                  int nrPasi = 3;
                  int nrProcesoare = 5;
                  public delegate void ProcessWork(object state);
                  ArrayList asyncResults;
                  WaitHandle[] waitHandles;
                  
                  static void Main(string[] args)
                  {
                        ThreadsTest c = new ThreadsTest();
                  }

                  public ThreadsTest()
                  {
                        asyncResults=new ArrayList(nrProcesoare);
                        waitHandles=new WaitHandle[nrProcesoare];
                        for(int pasCurent=0;pasCurent<nrPasi;pasCurent++)
                        {
                              Console.WriteLine("Starting pass {0}...",pasCurent);
                              int tick=Environment.TickCount;
                              for(int i=0; i<nrProcesoare; i++)
                              {
                                    Procesor p = new Procesor(i, pasCurent);
                                    ProcessWork processWork=new ProcessWork(p.Work);
                                    asyncResults.Add(processWork.BeginInvoke(null,null,null));
                                    waitHandles[i]=((IAsyncResult)asyncResults[i]).AsyncWaitHandle;
                              }
                              WaitHandle.WaitAll(waitHandles);
                              Console.WriteLine("pas " + pasCurent + ", wait: " + (Environment.TickCount - tick));
                              asyncResults.Clear();
                        }
                        Console.ReadLine();
                  }      
      
            }

            public class Procesor
            {
                  int procesorCurent;
                  int pasCurent;

                  public Procesor(int procID, int pasCurent)    
                  {
                        procesorCurent = procID;
                        this.pasCurent=pasCurent;
                  }

                  public void Work(object state)
                  {
                        Console.WriteLine("Procesor " + procesorCurent + " pasul " + pasCurent);
                  }
            }
      }

}
0
 

Author Comment

by:fulgeru99
ID: 11024999
Nice work AndreSteffens, but but what you do there is king of  a 'cheat'.

1. If you put 50 Proecessors, you actualy have only one at a moment because you initialize it, it does the Console.WriteLine and then the thread is killed, so for the next stpe you start another thread that you initialize with another pasCurent.
    If you insert the ThreadCount column in the Windows Task Manager and insert a delay after launching the threads you will see that the nr of threads never changes. Try the same on my code and you will see the number growing.
    Actualy the problem is the folowing: those Procesor classes may have a lot of data in them, so if you Procesor p = new Procesor(i, pasCurent); every step, aditional data may not be pased to the Procesor class.
     Your solutiopn is alse time consuming because those initializauins and diposes of threads are not eficient.

2. You do not use the default thread pool asociated with the aplication. See the BeginInvoke help page in MSDN:
.... The BeginInvoke method calls the specified delegate back on a different thread pool thread. You should not block a thread pool thread for any length of time.

0
 
LVL 10

Expert Comment

by:ptmcomp
ID: 11025433
I think the thread pool threads should be used to comlete workitems but not for long running synchronized tasks. Therefore you should create your own threads.
0
 

Author Comment

by:fulgeru99
ID: 11025523
As I said before, I've done it with the Thread class, but I wanna know how to do it with the ThreadPool.
0
 
LVL 4

Accepted Solution

by:
AndreSteffens earned 110 total points
ID: 11025595
Well, talking about getting a pat on the head and a slap in the face in once sentence...
As you remark yourself, it is not efficient to use the threadpool for long running processes, so your remark of 'suppose the workerthread has to process lots of data, than your code is inefficient' is not valid. As Ptmcomp says: in that case you should give your Processors their own threads and yes you already know how to do that.
My code sample produces exactly the same output as your threadpool code AND eliminates the first pass overhead. So I stand firm in my opinion that I answered your question...
0
 

Author Comment

by:fulgeru99
ID: 11033737
sorry man but you actualy create 1 thread, then kill him, and then create another one, and so on. this not waht i was looking for
0
 
LVL 4

Expert Comment

by:AndreSteffens
ID: 11033878
Then I stand defeated :-) You are a tough customer...
0
 
LVL 10

Assisted Solution

by:ptmcomp
ptmcomp earned 110 total points
ID: 11034258
You wrote some time ago: "I have solved the problem using Thread class instances".
I think that was the best solution. What are we still searching for?
0
 

Author Comment

by:fulgeru99
ID: 11034274
Well, I just want to see all the possible solutions.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now