views:

172

answers:

4

Hello all,I am learning multi-threading and for the sake of understanding I have wriiten a small function using multithreading...it works fine.But I just want to know if that thread is safe to use,did I followed the correct rule.

void CThreadingEx4Dlg::OnBnClickedOk()
{
    //in thread1 100 elements are copied to myShiftArray(which is a CStringArray)
    thread1 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction1,this);
    WaitForSingleObject(thread1->m_hThread,INFINITE);
    //thread2 waits for thread1 to finish because thread2 is going to make use of myShiftArray(in which thread1 processes it first)
    thread2 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction2,this);
    thread3 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction3,this);

}

UINT MyThreadFunction1(LPARAM lparam)
{
    CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
    pthis->MyFunction(0,100);
    return 0;
}
UINT MyThreadFunction2(LPARAM lparam)
{
    CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
    pthis->MyCommonFunction(0,20);
    return 0;
}

UINT MyThreadFunction3(LPARAM lparam)
{
    CThreadingEx4Dlg* pthis = (CThreadingEx4Dlg*)lparam;
    WaitForSingleObject(pthis->thread3->m_hThread,INFINITE);
    //here thread3 waits for thread 2 to finish so that thread can continue
    pthis->MyCommonFunction(21,40);
    return 0;
}
void CThreadingEx4Dlg::MyFunction(int minCount,int maxCount)
{

    for(int i=minCount;i<maxCount;i++)
    {
        //assume myArray is a CStringArray and it has 100 elemnts added to it.
        //myShiftArray is a CStringArray -public to the class
        CString temp;
        temp = myArray.GetAt(i);
        myShiftArray.Add(temp);
    }

}

void CThreadingEx4Dlg::MyCommonFunction(int min,int max)
{
    for(int i = min;i < max;i++)
    {
        CSingleLock myLock(&myCS,TRUE);
        CString temp;
        temp = myShiftArray.GetAt(i);
        //threadArray is CStringArray-public to the class
        threadArray.Add(temp);
    }
    myEvent.PulseEvent();

}
+2  A: 

Which function do you intend to be "thread-safe"?

I think that the term should be applied to your CommonFunction. This is a function that you intend to be called be several (two in this first case) threads.

I think your code has a rule on the lines of:

Thread 2 do some work

meanwhile Thread 3 wait until Thread 2 finishes then you do some work

In fact your code has

WaitForSingleObject(pthis->thread3->m_hThread,INFINITE);

maybe waits for the wrong thread?

But back to thread safety. Where is the policing of the safety? It's in the control logic of your threads. Suppose you had lots of threads, how would you extend what you've written? You have lots of logic of the kind:

if thread a has finished and thread b has finished ...

Really hard to get right and maintain. Instead you need to make CommonFunction truly thread safe, that is it needs to tolerate being called by several threads at the same time.

In this case you might do that by putting some kind of mutex around the critical part of the code, which perhaps in this case is the whole function - it's not clear whether you intend to keep the items you copy together or whether you mind if the values are interleaved.

In the latter case the only question is whether access to myArray and myShiftArray are thread safe collections

    temp = myArray.GetAt(i);
    myShiftArray.Add(temp);

all your other variables are local, on the stack so owned by current threads - so you just need to consult the documentation for those collections to determine if they can safely be called by separate threads.

djna
thanks for your reply,?I dont understand the point "maybe waits for the wrong thread"..how can it wait for the wrong thread..I have given the handle for thread3..then how could it go wrong..can u please explain that point?
kiddo
maybe I misunderstand your code (I do Java not .Net) I wasn't sure whether the code WaitForSingleObject(pthis->thread3->m_hThread should pass the thread that is waiting: thread3, or the thread it's waiting for to finish: thread2.
djna
oops sorry..Its my mistake..I just noticed that....
kiddo
A: 

It looks like this is going to work because the threads aren't going to do any work concurrently. If you change the code to make the threads perform work at the same time, you will need to put mutexes (in MFC you can use CCriticalSection for that) around the code that accesses data members which are shared between the threads.

Alex - Aotea Studios
thanks for your reply,I did used CCriticalSection...please check the "MyCommonFunction"..and like you said threads are not going to work concurrently..I do know that.I wrote this code to understand the concept of CEvent and WaitForSingleObject
kiddo
+2  A: 

You did make it thread-safe but you effectively prevented benefits of threading completely. Your Thread functions will not be executing simultaneously but will be waiting for the previous thread to terminate first instead. It's essentially a sequential execution, not threaded.

Even if you run ThreadFunction2 and 3 simultaneously (working on separate parts of the array), you are acquiring a lock too early in the loop that would not benefit from reading from the array in parallel.

Instead you should acquire a lock only before adding to the threadArray since it's the common write destination and release the lock immediately after writing is complete. This would be faster but gains from multi-threading would be marginal unless you introduce some computation before adding to threadArray.

ssg
thanks for your reply..My main concern here is about CEvent and WaitforSingleObject..as you all said threading in my module are of no use..I do agree that..and also I appreciate your point on using the lock..Can you tell me an example situation to use CEvent/waitformultipleobject?
kiddo
It's not part of your question but Event and WaitMultiple's can be used for dispatching and scheduling. Let's say you have a pool of 10 threads and each thread processes given sub-block. You want to be notified when one of them finishes execution so you can schedule another one to process another block. You do this by doing a WaitMultiple on all 10 objects. Alternative way would be "polling" which is very CPU expensive, or "waiting for a fixed amount time" which is very inefficient. Multiplewait becomes ideal in that case.
ssg
A: 

As I've pointed out before what you are doing is entirely pointless you may as well not use threads as you fire a thread off and then wait for the thread to complete before doing anything further.

You give precious little information about your CEvent but your WaitForSingleObjects are waiting for the thread to enter a signalled state (ie for them to exit).

As MyCommonFunction is where the actual potentially thread un-safe thing occurs you have correctly critical sectioned the area, however, threads 2 and threads 3 don't run concurrently. Remove the WaitForSingleObject from MyThreadFunction3 and then you will have both running concurrently in a thread-safe manner, thanks to the critical section.

That said its still a tad pointless as both threads are going to spend most of their time waiting for the critical section to come free. In general you want to structure threads so that there is precious little they need to hit critical sections for and then, when they hit a critical section, hit it only for a very short time (ie not the vast majority of the function's processing time).

Edit:

A Critical section works by saying I'm holding this critical section anything else that wants it has to wait. This means that Thread 1 enters the critical section and begins to do what it needs to do. Thread 2 then comes along and says "I want to use the critical section". The kernel tell its "Thread 1 is using the critical section you have to wait your turn". Thread 3 comes along and gets told the same thing. Threads 2 and 3 are now in a wait state waiting for that critical section to come free. When Thread 1 finishes with the critical section both Threads 2 and 3 race to see who gets to hold the critical section first and when one obtains it the other has to continue waiting.

Now in your example above there would be so much waiting for critical sections it is possible that Thread 1 can be in the critical section and Thread 2 waiting and before Thread 2 has been given the chance to enter the critical section Thread 1 has looped back round and re-entered it. This means that Thread 1 could end up doing all its work before Thread 2 ever gets a chance to enter the critical section. Therefore keeping the amount of work done in the critical section compared to the rest of the loop/function as low as possible will aid the Threads running simultaneously. In your example one thread will ALWAYS be waiting for the other thread and hence just doing it serially may actually be faster as you have no kernel threading overheads.

ie the more you avoid CriticalSections the less time lost for threads waiting for each other. They are necessary, however, as you NEED to make sure that 2 threads don't try and operate on the same object at the same time. Certain in-built objects are "atomic" which can aid you on this but for non-atomic operations a critical section is a must.

An Event is a different sort of synchronisation object. Basically an event is an object that can be one of 2 states. Signalled or not-signalled. If you WaitForSingleObject on a "not-signalled" event then the thread will be put to sleep until it enters a signalled state.

This can be useful when you have a thread that MUST wait for another thread to complete something. In general though you want to avoid using such synchronisation objects as much as possible as it destroys the parallel-ness of your code.

Personally I use them when I have a worker thread waiting for when it needs to do something. The Thread sits in a wait state most of its time and then when some background processing is required I signal the event. The thread then jumps into life and does what it needs to do before looping back round and re-entering the wait state. You can also mark a variable as indicating that the object needs to exit. This way you can set an exit variable to true and then signal the waiting thread. The waiting thread wakes up and says "I should exit" and then exits. Be warned though that you "may" need a memory barrier that says make sure the exit variable is set before the event is woken up otherwise the compiler might re-order the operations. This could end up leaving your thread waking up finding out that the exit variable isn't set doing its thing and then going back to sleep. However the thread that originally sent the signal now assumes the thread has exited when it actually hasn't.

Whoever said multi-threading was easy eh? ;)

Goz
I clearly understand the difference b/w the CriticalSection and CEvent Synchroonization's...but I am confused how and when to use it...Can give me a simple example by using criticalsection and CEvent(to signal)..thanks in advance
kiddo
Well I'm not entirely sure its easy to give a "simple" example ...
Goz
just to understand...in what kind of situation's we use it?
kiddo
Well I've updated my answer ... I could go on and on. Its a pretty in-depth area ;)
Goz
thank you very much...I have to work-out more and more examples and should add some twists and turns(like MKnight Shamalon film) to play around multi-threading...
kiddo