Thread: CreateThread ?!

  1. #1
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768

    CreateThread ?!

    I have a couple of questions on what to do when I want to run multiple threads at the same time...

    1.) Can I use the same created semaphore for of the threads running at the same time?
    Code:
    CreateSemaphore(NULL, 4, 4, THREAD_SEMAPHORE);
    2.) Can I use the same lpThreadId, or should I use different pointers?
    Code:
    HANDLE CreateThread(
      LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to security attributes
      DWORD dwStackSize,                         // initial thread stack size
      LPTHREAD_START_ROUTINE lpStartAddress,     // pointer to thread function
      LPVOID lpParameter,                        // argument for new thread
      DWORD dwCreationFlags,                     // creation flags
      LPDWORD lpThreadId                         // pointer to receive thread ID
    );

    Thank you.
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

  2. #2
    Unregistered User
    Join Date
    Sep 2005
    Location
    Antarctica
    Posts
    341
    it doesn't matter what you give to lpThreadId as long as it's a valid pointer and you don't care to use the data it returns. What are you using hte semaphore for? If you want to use it to synchronize the threads then you will have to use the same for all them. I usually use CriticalSections for synchronizing, but with a semaphore you can specify how many threads are able to access the same code at the same time, I've never had a reason to let more than one thread access a critical section at the same time though.

  3. #3
    Disrupting the universe Mad_guy's Avatar
    Join Date
    Jun 2005
    Posts
    258
    The lpThreadId parameter of the CreateThread function should always be used for backwards compatability (MSDN: "Windows Me/98/95: This parameter may not be NULL") with older Windows systems. You do not nessecarily have to use it, it just has to be a pointer to a DWORD, normally though, I always give some kind of message in my diagnostics programs stating the thread ID to make my code more opaque during runtime.


    As for the semaphore question: you should REALLY question using Semaphores, Mutexes or Events in comparison with something like the Interlocked functions or Critical Sections. Mutexes and Semaphores are what a big windows programmer would call 'kernel objects.' Kernel objects exist as far as the kernel is considered, and they're called kernel objects because the kernel synchronizes access to them using kernel synchronization functions (namely, the WaitFor[Single|Multiple]Object[s](...) function and the Release*(...) functions). The astute reader would realize that naturally for the kernel to schedule such things it would take more CPU time, therefore, you should use them when you need them.


    Impractical use of kernel object synchronization between threads
    Using them to access a simple global variable. Example:

    Code:
    MYTYPE g_x;
    HANDLE g_hMutex;
    
    //...
    
    void funcinator1() {
    -- WaitForSingleObject(g_hMutex,INFINATE);
    //Computate on g_x;
    -- ReleaseMutex(g_hMutex);
    }
    
    
    void funcinator2() {
    -- WaitForSingleObject(g_hMutex,INFINATE);
    //Computate on g_x;
    -- ReleaseMutex(g_hMutex);
    }
    
    int WINAPI WinMain(...) {
    -- g_hMutex = CreateMutex(NULL,FALSE,NULL);
    -- HANDLE hThread1 = CreateThread(...,(LPTHREAD_START_ROUTINE)funcinator1,...);
    -- //...
    -- HANDLE hThread1 = CreateThread(...,(LPTHREAD_START_ROUTINE)funcinator2,...);
    -- //...
    }
    This code uses kernel objects to synchronize access to a simple global object, when critical sections will do. Kernel objects take more time to synchronize than something like a critical section, and therefore should be used when you have to wait on a kernel object. A kernel object is created and associated with a HANDLE when you call a function that creates a kernel object, the simplest example is a Thread. A call to CreateThread creates a kernel object and it is associated with the returned HANDLE, therefore it is logical to be able to use a Wait function to wait for that HANDLE to return because a kernel object is created on CreateThread and is released when the thread returns.

    Correct usage of kernel objects
    Using them to wait for something like a thread to return. Example:

    Code:
    void funcinator() {
    -- //...
    }
    
    int WINAPI WinMain(...) {
    -- HANDLE hThread = CreateThread(...,(LPTHREAD_START_ROUTINE)funcinator,...);
    -- WaitForSingleObject(hThread,INFINATE);
    }

    A semaphore would be useful in this canonical situation: writing a webserver. You could have it so that a call to CreateSemaphore lets 3 threads through and has them handle requests. So on a clients' request, one thread would be created and it would wait for a semaphore and if it is open it will be let through to process the clients request. But if 3 clients are already being handled, if a fourth client connects the thread will be created, but it will have to wait for another thread to let go of it's semaphore object and decrease it's lock count (and logically, when a semaphore is 'grabbed it's lock count increases.)

    Mutexes apply the same way. They (like Semaphores) ensure that a thread has exclusive access to a resource (contra semaphores in which x amount of threads can access one resource.) But like said earlier, why would you need a mutex if a critical section already exists because they have very similar semantics? Well, for one, you can wait on a mutex object for x milliseconds before giving up, while with a critical section you have to wait, so you could have an application wait for an object for 100 milliseconds in a loop, and when that expires process it's messages then continuing in that loop (of course, you can have a thread wait for a critical section and continue immediately if it's not available, but that's not the point.) Second, mutexes are cross-process objects. I can use CreateRemoteThread to create a thread in a remote process, but I'll still have the ability to use WaitForSingleObject on it because the HANDLE returned is associated with that kernel object.



    Again, question the use of semaphores, they're extremely slow compared to something like a Critical section, but if you need x amount of threads to run something while the others wait, then you should use a Semaphore.



    EDIT: A side note on threading: don't do it unless it's not possible any other way, I might be stating the obvious, but seriously, exhaust every resource (pun not intended) you have before having to resorting to threads. You easily tack on weeks of debugging and auditing for the addition of even a single thread. If you have to, use fibers. Fibers are like 'miniature' threads within threads. A process has threads, and threads have fibers. Ergo:

    Process
    |
    +--Thread1
    |
    +Fiber1
    +Fiber2
    +Fiber3
    +Fiber4
    +--Thread2
    [No fibers]

    Fibers are practically like threads, only you define your OWN scheduling algorithm, because the kernel synchronizes threads, but fibers don't exist as far as the kernel is concerned. Their context switches are also much less expensive than a Threads' is, the only thing is be very hesitent about mixing threads with fibers. Because if in the above example, Fiber 1 of thread 1 calls WaitForSingleObject on a resource that Thread2 at that point in time owns,Thread1 hault's, and so do all of it's fibers. This is because WaitForSingleObject has what we call Thread Affinity meaning it pauses the thread, and if you use fibers, fibers compose the thread itself (actually when you call ConvertThreadToFiber I guess you could say the thread doesn't exist anymore, more like a 'psuedo thread,' and it exists so the kernel knows where to return so the fibers can then take over via the internal scheduler, because, again, the kernel doesn't know fibers exist and therefore can't return to one of them.) Fibers also don't need any resource synchronization between fibers because the resource will always be owned by the Thread. You use fibers by calling ConvertThreadToFiber, then calling CreateFiber and finally switching between fibers via the SwitchToFiber function.
    Last edited by Mad_guy; 11-05-2005 at 11:40 PM.
    operating systems: mac os 10.6, debian 5.0, windows 7
    editor: back to emacs because it's more awesomer!!
    version control: git

    website: http://0xff.ath.cx/~as/

  4. #4
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768
    I'm still trying to learn about working with Threads, and one day I hope to understand them the way you do, but at the moment I wish to know; Can I create a single semaphore when I attempt to run x threads side by side while they need to access the same global variables?
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

  5. #5
    Unregistered User
    Join Date
    Sep 2005
    Location
    Antarctica
    Posts
    341
    Yes you can, but like he said, you probably don't want a semaphore. What I would suggest to do is to create a CRITICAL_SECTION. So, globally, you declare your critical section and your global data that you want to share:

    Code:
    CRITICAL_SECTION lock;
    int my_counter;
    if you want to, say, update a variable counter, you would want to protect it in a critical section. Before you can use this critical section, you must first initialize it, but only once in your program.

    Code:
    int WinMain(...)
    {
        InitializeCriticalSection(&lock);
    }
    So then for your threads, you would want to make sure that you don't try to access the global var more than once at the same time:

    Code:
    DWORD WINAPI MyThread1(LPVOID p)
    {
        EnterCriticalSection(&lock);
        my_counter++;
        LeaveCriticalSection(&lock);
    
        ...
    
        EnterCriticalSection(&lock);
        my_counter--;
        LeaveCriticalSection(&lock);
    }
    Code:
    DWORD WINAPI MyThread2(LPVOID p)
    {
        EnterCriticalSection(&lock);
        my_counter++;
        LeaveCriticalSection(&lock);
    
        ...
    
        EnterCriticalSection(&lock);
        my_counter--;
        LeaveCriticalSection(&lock);
    }
    That's the general idea, you need to use the same object between the threads to synchronize with. There are actually better functions to do this specific thing (InterlockedIncrement/InterlockedDecrement), so this is just to show you how you would use a critical section generally.
    Last edited by rockytriton; 11-08-2005 at 05:32 PM.

  6. #6
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768
    Can you please explain alittle more about the critical section, because I don't really understand how does it stop me from using my_counter more than once at the same time? It's not like it's bind in some way to lock, no?
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

  7. #7
    Unregistered User
    Join Date
    Sep 2005
    Location
    Antarctica
    Posts
    341
    when you call EnterCriticalSection and then another thread calls EnterCriticalSection with the same CRITICAL_SECTION pointer, the second thread will wait before exiting the function until the first thread calls LeaveCriticalSection to "unlock" the pointer so that it can be locked again.

    The best way to understand it is to create a little program that uses it, create two threads, have them both enter the critical section, print a line, sleep for 10 seconds, then print another line and leave the critical section, you will see that the second thread to enter will not print any output until the first one has finished and left the critical section.
    Last edited by rockytriton; 11-11-2005 at 10:22 AM.

  8. #8
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768
    So in other words, I don't need any semaphore, should I really use threads without a semaphore that will allow them to share variables?
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

  9. #9
    Unregistered User
    Join Date
    Sep 2005
    Location
    Antarctica
    Posts
    341
    it's not that these thread synchronization techniques "allow" threads to share data, it's that they provide a way to ensure that threads who share data do not corrupt the data by modifying it concurrently.

  10. #10
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768
    Ok, I'm alittle confused at the moment... Let me see if I got this right;

    A semaphore is used to control the number of threads to access the same resources at the same time (a thread count limitation).
    While EnterCriticalSection is for control over critical code, to forbid different threads that share the same semaphore to access the same shared resources at the same time.

    So what is the right way to work with multiple threads running at the same time and accessing the same global variables? Should I continue using a semaphore, but also add EnterCriticalSection to my code?

    Is there any better way, except using fibers?
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

  11. #11
    Unregistered User
    Join Date
    Sep 2005
    Location
    Antarctica
    Posts
    341
    do you want more than one thread to modify the data at the same time? If so, use a semaphore, if you no more than one thread to modify the shared data at the same time, then use a critical section, now if you want no more than one process to modify the shared data at the same time (such as a file), use a mutex.

  12. #12
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768
    So I shouldn't mix between a Ctritical Section and a Semaphore?
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

  13. #13
    Unregistered User
    Join Date
    Sep 2005
    Location
    Antarctica
    Posts
    341
    no, they aren't the same, a semaphore lets you have more than one thread access the data if you wish, but you can limit it to one as well I believe, I've never used them, only read about them. I've always used either critical sections or mutexes.

  14. #14
    * Death to Visual Basic * Devil Panther's Avatar
    Join Date
    Aug 2001
    Posts
    768
    it's not really what i'm asking, what I what to know is, should I use semaphores to limit the number of threads to access the same data and also use Critical Section to avoid mistakes?
    "I don't suffer from insanity but enjoy every minute of it" - Edgar Allen Poe

    http://www.Bloodware.net - Developing free software for the community.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. CreateThread() function question
    By chiefmonkey in forum C++ Programming
    Replies: 5
    Last Post: 05-15-2009, 07:52 AM
  2. compiler doesn't like CreateThread
    By finkus in forum C++ Programming
    Replies: 6
    Last Post: 11-30-2005, 03:06 AM
  3. sending arguments to function using CreateThread
    By Micko in forum C++ Programming
    Replies: 3
    Last Post: 07-15-2004, 07:07 PM
  4. CreateThread and lpParam
    By Hunter2 in forum Windows Programming
    Replies: 6
    Last Post: 06-02-2004, 04:46 PM
  5. pass several arguments to LPVOID param of CreateThread
    By Echidna in forum Windows Programming
    Replies: 4
    Last Post: 05-05-2002, 10:18 AM