views:

91

answers:

2

How would you set the object data that is shared between threads and needs to be updated once after the complete cycle of (say) two threads in busy loop?

CRITICAL_SECTION critical_section_;

int value; //needs to be updated once after the cycle of any number of threads running in busy loop

void ThreadsFunction(int i)
{

 while (true)
 {
  EnterCriticalSection(&critical_section_);
                /* Lines of Code */
  LeaveCriticalSection(&critical_section_);
 }
}

Edit: The value can be an object of any class.

+2  A: 

If you want implement thread safe update of an integer you should better use InterlockedIncrement and InterlockedDecrement or InterlockedExchangeAdd functions. See http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx.

If you do need use EnterCriticalSection and LeaveCriticalSection you will find an example in http://msdn.microsoft.com/en-us/library/ms686908(v=VS.85).aspx, but I recommend you to use EnterCriticalSection inside of __try block and LeaveCriticalSection inside of the __finally part of this blocks.

Oleg
Please note that the interlocked functions are SERIOUSLY hard to get right. If all you're doing is incrementing or decrementing a variable, they're pretty safe but it's astonishingly hard to use them correctly. In all seriousness, I only know of a dozen or so folks at MSFT who I'd trust to write lock free code (and I'm not one of them).You're safer with just entering and leaving the critical section. If you start seeing excessive CPU usage due to context switches, initialize the critsec with a spin count and it should help.
Larry Osterman
@Larry. Sorry but I don't understand what is so complex in using of interlocked functions. They are extremely easy to understand and they has the best performance in all processor architecture.
Oleg
Oleg: The best example I know of is the "double checked lock pattern". On the surface, the DCLP is safe, but as this: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html article explains, it's actually a broken pattern.Herb Sutter has a great column on concurrency referenced here: http://herbsutter.com/2008/08/05/effective-concurrency-lock-free-code-a-false-sense-of-security/
Larry Osterman
A: 

Two suggestions:

  • Make the object itself thread safe.
  • Pass the object into the thread as instance data

I'll use C++ as a reference in my example. You can easily transpose this to pure C if you want.

// MyObject is the core data you want to share between threads

struct MyObject
{
   int value;
   int othervalue;
   // all all the other members you want here
};


class MyThreadSafeObject
{
private:
    CRITICAL_SECTION _cs;
    MyObject _myojbect;
    bool _fLocked;
public:
    MyThreadSafeObject()
    {
        _fLocked = false
        InitializeCriticalSection();
    }
    ~MYThreadSafeObject()
    {
        DeleteCriticalSection();
    }

    // add "getter and setter" methods for each member in MyObject
    int SetValue(int x)
    {
         EnterCriticalSection(&_cs);
             _myobject.value = x;
         LeaveCriticalSection(&_cs);
    }

    int GetValue()
    {
         int x;
         EnterCriticalSection(&_cs);
             x = _myobject.value;
         LeaveCriticalSection(&_cs);
         return x;
    }

    // add "getter and setter" methods for each member in MyObject
    int SetOtherValue(int x)
    {
         EnterCriticalSection(&_cs);
             _myobject.othervalue = x;
         LeaveCriticalSection(&_cs);
    }

    int GetOtherValue()
    {
         int x;
         EnterCriticalSection(&_cs);
             x = _myobject.othervalue;
         LeaveCriticalSection(&_cs);
         return x;
    }


    // and if you need to access the whole object directly without using a critsec lock on each variable access, add lock/unlock methods
    bool Lock(MyObject** ppObject)
    {
        EnterCriticalSection(&_cs);
        *ppObject = &_myobject;
        _fLocked = true;
        return true;                
    }

    bool UnLock()
    {
        if (_fLocked == false)
            return false;

        _fLocked = false;
        LeaveCriticalSection();
        return true;
    }
};

Then, create your object and thread as follows:

MyThreadSafeObject* pObjectThreadSafe;
MyObject* pObject = NULL;

// now initilaize your object
pObjectThreadSafe->Lock(&pObject);
   pObject->value = 0; // initailze value and all the other members of pObject to what you want them to be.
   pObject->othervalue = 0;
pObjectThreadSafe->Unlock();
pObject = NULL;


// Create your threads, passing the pointer to MyThreadSafeObject as your instance data
DWORD dwThreadID = 0;
HANDLE hThread = CreateThread(NULL, NULL, ThreadRoutine, pObjectThreadSafe, 0, &dwThreadID);


And your thread will operate as follows
DWORD __stdcall ThreadFunction(void* pData)
{
    MyThreadSafeObject* pObjectThreadSafe = (MyThreadSafeObject*)pData;
    MyObject* pObject = NULL;

    while (true)
    {
       /* lines of code */
           pObjectThreadSafe->SetValue(x);
       /* lines of code */         
    }

}
selbie
One nit: You might want to use an auto-lock (see ATL's CCritSecLock for an example) instead of explicit lock and release APIs - it's harder to get them wrong.
Larry Osterman