Click to See Complete Forum and Search --> : Thread syncronization
C#er
July 3rd, 2007, 11:23 AM
Hi for all
I've created a program that triggers some threads.
I don't get to use the syncronization. Some threads are triggered before others.
I created a class called ResourceObject. This is a simple class with a boolean value and a CRITICAL_SECTION value:
ResourceObject::ResourceObject()
{
m_bLocked = false;
InitializeCriticalSection(&m_ResourceSection);
}
ResourceObject::~ResourceObject()
{
DeleteCriticalSection(&m_ResourceSection);
}
void ResourceObject::LockResource(unsigned int uiTimeToSleep)
{
EnterCriticalSection(&m_ResourceSection);
m_bLocked = true;
Sleep(uiTimeToSleep);
LeaveCriticalSection(&m_ResourceSection);
}
void ResourceObject::ReleaseResource()
{
EnterCriticalSection(&m_ResourceSection);
if(m_bLocked)m_bLocked = false;
LeaveCriticalSection(&m_ResourceSection);
}
In another class I have this struct that have a pointer to a ResourceObject
typedef struct LineText
{
FloatText * Line;
bool bShow;
float fTimeWait;
ResourceObject * resource;
}TextLine;
In the same class I have the following codes:
- the first is how I create a thread;
- the second is the thread:
- the third is when I Resume the threads and show some text;
idThread[m_iLineCount] = CreateThread(NULL, 0, Helper::TextThread, (void*)&Lines[m_iLineCount], CREATE_SUSPENDED, NULL);
DWORD WINAPI Helper::TextThread(void * vData)
{
LineText * temp = (LineText *)vData;
temp->resource->LockResource(2000);
temp->bShow = true;
temp->resource->ReleaseResource();
return 0;
}
void Helper::ShowImages()
{
for(int i = 0 ; i < m_iLineCount ; i++)
{
ResumeThread(idThread[i]);
if(Lines[i].bShow)Lines[i].Line->PrintText();
}
}
The threads must have to follow the sequence in that they are resumed, but they don't. Even the first thread is started with the resource, it is not the first to show the text.
Anyone can help me to solve this problem?
Thanks
ncode
July 4th, 2007, 08:59 AM
1) use common synchronization objects wrappers.
You can see them in boost or ATL libraries or look at my wrappers:
//TNonCopyable - base class for noncopyable objects, taken from boost library
class TNonCopyable
{
protected:
TNonCopyable() {}
~TNonCopyable() {}
private: //emphasize the following members are private
TNonCopyable(const TNonCopyable&);
const TNonCopyable& operator=(const TNonCopyable&);
};
//TCriticalSection - critical section object for recursive locking strategy
class TCriticalSection : private TNonCopyable
{
friend class TCriticalSectionLock;
public:
//init
TCriticalSection() {::InitializeCriticalSection(&Object);}
~TCriticalSection() {::DeleteCriticalSection(&Object);}
private:
CRITICAL_SECTION Object;
//operation
void Lock() {::EnterCriticalSection(&Object);}
void Unlock() {::LeaveCriticalSection(&Object);}
};
//TCriticalSectionLock - locks critical section during object lifetime
class TCriticalSectionLock : private TNonCopyable
{
public:
TCriticalSectionLock(TCriticalSection& critical_section) :
CriticalSection(critical_section) {CriticalSection.Lock ();}
~TCriticalSectionLock() {CriticalSection.Unlock();}
private:
TCriticalSection& CriticalSection;
};
2) Use common practice to write "thread safe" classes. All public methods of such class which have access to shared resource must be protected by critical section. Then you can call these methods (simultaneously) from different threads, without worring about resource corruption.
For example look at this "thread safe" class example.
template<typename T> class TMessageQueueT : private TNonCopyable
{
protected:
std::list<T> Queue; //shared resource
TCriticalSection CriticalSection; //synchronization object
public:
//ctor/dtor
TMessageQueueT() {}
~TMessageQueueT() {Clear();}
//operations
void PushBack(const T& message)
{
//protect method call by critical section
TCriticalSectionLock lock(CriticalSection);
Queue.push_back(message);
}
bool Pop(T& message)
{
//protect method call by critical section
TCriticalSectionLock lock(CriticalSection);
if(true == Queue.empty())
return false;
message = Queue.front();
Queue.pop_front();
return true;
}
void Clear()
{
//protect method call by critical section
TCriticalSectionLock lock(CriticalSection);
Queue.clear();
}
};
C#er
July 5th, 2007, 10:49 AM
nCode, like you've said in my code I'm using EnterCriticalSection, LeaveCritical Section and so on. The problem is that what the MSDN said in this link
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/critical_section_objects.asp:
...
There is no guarantee about the order in which waiting threads will acquire ownership of the critical section.
I was thinking to use mutex. I've tried and the problem remains.
I was triying to install the boost in the Visual C++ 6.0 following the boost docs but it's too complicated.
Can you help again?
Sorry, I'm little bit new in use threads.
JVene
July 5th, 2007, 02:38 PM
The threads must have to follow the sequence in that they are resumed, but they don't.
What I get from this statement is that you have a sequence of events to be performed in order.
You've discovered that when a critical section or mutex is 'released' from a lock, any pending threads are released according to the task scheduling system of the operating system, which isn't "in order" relative to their acquisition of the pending lock queue. For one, the thread selected for execution might be given preference by thread priority (and that's not a hint for any predictable solution).
What you're describing is the need to control a series of processes through a queue. This will be an application level object, not an OS object like the critical section or mutex.
You can use, as a design example, an STL vector to store a collection of function pointers, or objects, that represent the series of steps to be performed. You can form more complex tree's if required (that is, the interdependency chain of even scheduling could be more complex than a simple list, such that a few prerequisite steps might lead to a number of steps that can be run in parallel, which then lead to a series of steps dependent on their completion).
For that, you'll need some kind of 'executive' which manages this. Basically this 'executive' operates the queue - understands the rules of interdependency and schedules processes for execution.
That is, you would use this executive to 'feed' a function to a thread (or threads) when appropriate.
This can be as simple as what I call a QueProcessor. In my own library:
QueProcessor q1;
Is all that's required to create a thread, which launches itself and waits for instructions. I could then submit with:
q1.Submit( o1, &MyObject::Step1 );
q1.Submit( o1, &MyObject::Step2 );
q1.Submit( o1, &MyObject::Step3 );
Which, in this case, are assumed to be 3 functions accepting void and returning void. The 3 submit statements are non-blocking. The calling thread goes on.
The thread represented by q1 will call Step1, and when that's completed q1 will call Step2, etc....
Q1 is accepting a pointer to a member function and an object (assumed in this example as MyObject o1 ). Other designs are possible, the point being you are submitting jobs to a queue to be executed in a simple list.
More complex rules about the relationship of these jobs can be established, and a more intelligent scheduler can operate upon them.
C#er
July 5th, 2007, 05:39 PM
Thanks for your help JVene.
I solved my problem using a watcher thread using this pseudocode:
Start WatcherThread;
foreach(thread in threadContainer)
{
start currentThread;
wait currentThread to be signaled;
threadContainer.nedtThread;
}
I think that my solution seems with your solution. Thanks for your help and for the nCode's help
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.