Thread can't count, giving wrong result

前端 未结 4 1046
闹比i
闹比i 2021-01-15 13:40

I wrote this piece of code

#include       /* Input/Output */
#include      /* General Utilities */
#include            


        
相关标签:
4条回答
  • 2021-01-15 13:53

    unsigned int cnt=0; is share-able among the threads and operation ++ is not atomically increase cnt. Two thread may read same values of cnt and increase, and overwrites cnt. You need to apply some concurrency control mechanism like semaphore, or mutex.


    If you would disassemble code using following command (suppose code name is thread1.c)

    ~$ gcc thread.c -lpthread -S  
    

    The output assembly code name is thread1.s.

    your wil find cnt++ is more then one instruction in low level in your code:

        movl    $0, -12(%ebp)
        jmp .L2
    .L3:
        movl    cnt, %eax
        addl    $1, %eax
        movl    %eax, cnt
        addl    $1, -12(%ebp)
    .L2:
        movl    NITERS, %eax
    

    (1) cnt fist move to %eax
    (2) then add one to %exc
    (3) move %eax into cnt back

    And due to thread context switching in between this line, same value of cnt is read by more than one threads. hence cnt++ is not atomic.

    Note: Global variable are thread shareable like cnt, and local variable like i that you declared in count() is thread specific.


    I modified your code and imposed concurrency control using semaphore, now it would work fine.

    Only modified code shown

    #include <pthread.h>    /* POSIX Threads */
    #include <semaphore.h>
    unsigned int cnt=0;  /*Count  variable%*/
    const int NITERS=1000;
    
    sem_t mysem;
    
    void count()
    {
        int i=0;
        for(i=0; i<NITERS; i++)
        {
            sem_wait(&mysem);
            cnt++;
            sem_post(&mysem);
        }
        pthread_exit(0);
    }
    int main()
    {
        if ( sem_init(&mysem,0,1) ) {
         perror("init");
        }
        // rest of your code 
    } 
    

    This will work good! some examples:

    nms@NMS:~$ ./thread 
    OK! cnt=2000
    nms@NMS:~$ ./thread 
    OK! cnt=2000
    nms@NMS:~$ ./thread 
    OK! cnt=2000
    
    0 讨论(0)
  • 2021-01-15 13:56

    Your 2 threads access a shared resource without protection thus a race conditions apply. The increase operation isn't atomic, so you can actually have something like this in terms of machine operations:

    Thread 1                   Thread 2
    Load value of cnt        
                               Load value of cnt
    Increase value of cnt      
    Write value of cnt         Increase value of cnt
                               Write value of cnt
    

    Note, that although both threads have increased the cnt, it was actually increased only by 1. If you want the result to be deterministic, you need to protect the shared resource (cnt), e.g. by locking it prior to access.

    0 讨论(0)
  • 2021-01-15 14:04

    You have a race condition problem.More info (I know it talks about visual basic, just skip these things)here.
    To solve it you need a mutex, declare it as global variable:

    pthread_mutex_t mux;  
    

    Initialize it:

    pthread_mutex_init(&mux,NULL);
    

    Then use it for reading the shared variable:

    void count()
    {
        int i=0;
        for(i=0; i<NITERS; i++)
        {
            pthread_mutex_lock(&mux);
            cnt++;
            pthread_mutex_unlock(&mux);
        }
        pthread_exit(0);
    }
    

    All this because there are two threads incrementing the same variable, they fetch the variables before incrementing it and they put it in a register.So they value that they are reading isn't unique: every thread has it's own copy so each thread may ignore the change of the other, until they effectively write it to it's address in the memory.
    NB : If you want that the thread update the variable in an ordered way (i.e.: thread 1 counts to NITERS without being interrupted, when thread 2 starts counting), you have to lock the mutex before the for.

    0 讨论(0)
  • 2021-01-15 14:08

    The increment operator is usually implemented by a read-modify-write, which is non-atomic.

    A non-atomic read-modify-write across threads can sometimes do this:

    Thread 1:    Thread 2:     count
    Read count   ...           1
    Add 1        Read count    1
    Write count  Add 1         2 
    ...          Write count   2
    

    Resulting in the count being less than expected.

    If you will access a shared resource across multiple threads, you need to protect it with some kind of threading aware locking mechanism, such as a mutex.

    0 讨论(0)
提交回复
热议问题