pthread Thread Knowledge Points

Keywords: Programming Unix Linux Mac OS X

pthread Thread Knowledge Points

The two fundamental issues of communication between threads are mutual exclusion and synchronization.`

#include<pthread.h>

1. pthread_create
1. Function Prototype
int pthread_create(pthread_t tidp,const pthread_attr_t attr,(void)(start_rtn)(void),void arg);

2. Functional functions:
pthread_create is a function of creating threads for Unix-like operating systems (Unix, Linux, Mac OS X, and so on).
Its function is to create a thread, which is essentially to determine the entry point to call the thread function, and run the related thread function once the thread is created.

On return to success, the memory unit pointed to by tidp is set to the thread ID of the newly created thread.The attr parameter is used to specify various thread properties.
The newly created thread runs from the address of the start_rtn function, which has only one universal pointer parameter, arg.
If you need to pass more than one parameter to the start_rtn function, you need to put these parameters into a structure and pass in the address of the structure as an argument to arg.

3. Return value:
Returns 0 for success and -1 for error.

4. Parameters
The first parameter is a pointer to the thread identifier.
The second parameter sets the thread properties.
The third parameter is the starting address of the thread's running function.
The last parameter is the parameter that runs the function.

Example:
//Thread Run Function
void say_hello(void args)
{
cout << "Hello Runoob!" << endl;
return 0;
}

int main()
{
    int indexes = 10;
    pthread_t tids;
    int ret = pthread_create(&tids, NULL, say_hello, (void *)&(indexe));
    pthread_exit(NULL);
}

2. pthread_join
1. Function Definition:
int pthread_join(pthread_t thread, void **retval);
2. Functional functions:
The pthread_join() subprogram blocks the caller until the specified threadid thread terminates

3. Thread Problems and Critical Zones
Now that we know how to create threads, let's take another look at an example where we create 100 threads that all access the same variable.
Half of them add 1 to this variable and half subtract 1 to make sense that the result will be 0.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREAD 100
void * thread_inc(void * arg);
void * thread_des(void * arg);
long long num = 0;   //The long long type is a 64-bit integer type, accessed by multiple threads

int main(int argc, char *argv[])
{
    pthread_t thread_id[NUM_THREAD];
    int i;

    //Create 100 threads, half thread_inc and half thread_des
    for(i = 0; i < NUM_THREAD; i++)
    {
        if(i %2)
            pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
        else
            pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
    }

    //Waiting for thread to return
    for (i = 0; i < NUM_THREAD; i++)
        pthread_join(thread_id[i], NULL);

    printf("result: %lld \n", num);  //+1, -1 logical result is 0
    return 0;
}

//Thread Entry Function 1
void * thread_inc(void * arg)
{
    for (int i = 0; i < 50000000; i++)
        num += 1;//Critical zone (the statement causing the problem is the critical zone location)
    return NULL;
}

//Thread Entry Function 2
void * thread_des(void * arg)
{
    for (int i = 0; i < 50000000; i++)
        num -= 1;//Critical zone
    return NULL;
}

//From the run results, it is not 0, and the results are different for each run.So what is the cause? 
//Because each thread accesses a variable in such a way that it takes its value from memory to the CPU,
//The CPU then calculates the changed value and writes it back to memory.So we can easily see that,
//Multiple threads access the same variable. If a thread has just taken data out of memory and has not yet had time to write back to memory, the other threads access the variable again, so this value is incorrect.

3.1,Next let's talk about how to solve this problem: thread synchronization
    //Mutex and semaphore.

    //Mutual exclusion:
    //Mutex technology can also be understood literally, that is, critical zone has thread access, other threads have to wait in queue, their access is mutually exclusive, and the way to achieve this is to lock and release the lock on the critical zone.

    #include <pthread.h>

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);  //Create Mutex

    int pthread_mutex_destroy(pthread_mutex_t *mutex);//Destroy Mutual Exclusion

    int pthread_mutex_lock(pthread_mutex_t *mutex);//Locking

    int pthread_mutex_unlock(pthread_mutex_t *mutex);//Release lock
    //The critical zone enclosure must correspond to lock and unlock one-to-one.
    /*Expand the critical zone, reduce locks, and release lock calls, but such variables must be filled up to 50,000,000 times before other threads can access them.
    This lengthens the wait time for threads, but shortens the time for locking and releasing lock function calls. There is no conclusion here. Consider it as appropriate*/
    void * thread_inc(void * arg)
    {
        pthread_mutex_lock(&mutex);  //Mutex Lock
        for (int i = 0; i < 1000000; i++)
            num += 1;
        pthread_mutex_unlock(&mutex); //Mutex Release Lock
        return NULL;
    }

    /*Thread wait times are reduced, but loops are created, and lock release function calls are increased*/
    void * thread_des(void * arg)
    {
        for (int i = 0; i < 1000000; i++)
        {
            pthread_mutex_lock(&mutex);
            num -= 1;
            pthread_mutex_unlock(&mutex);
        }
        return NULL;
    }

    //Semaphore:
    //Semaphores are similar to mutexes except that mutexes use locks to control thread access and semaphores use binary 0,1 to control thread order.
    sem_post Semaphore plus 1, sem_wait Decrease the semaphore by 1, when the semaphore is 0, sem_wait This causes the semaphore to be added 1 minus 1 to control the thread's execution order.

    #include <semaphore.h>

    int sem_init(sem_t *sem, int pshared, unsigned int value);//Create semaphore
    int sem_destroy(sem_t *sem);//Destroy semaphore
    int sem_post(sem_t *sem);//Semaphore plus 1
    int sem_wait(sem_t *sem);//Signal minus 1, blocking at 0

    #include <stdio.h>
    #include <pthread.h>
    #include <semaphore.h>

    void * read(void * arg);
    void * accu(void * arg);
    static sem_t sem_one;
    static sem_t sem_two;
    static int num;

    int main(int argc, char *argv[])
    {
        pthread_t id_t1, id_t2;
        sem_init(&sem_one, 0, 0);
        sem_init(&sem_two, 0, 1);

        pthread_create(&id_t1, NULL, read, NULL);
        pthread_create(&id_t2, NULL, accu, NULL);

        pthread_join(id_t1, NULL);
        pthread_join(id_t2, NULL);

        sem_destroy(&sem_one);
        sem_destroy(&sem_two);

        return 0;
    }

    void * read(void * arg)
    {
        int i;
        for (i = 0; i < 5; i++) {
            fputs("Input num: ", stdout);
            sem_wait(&sem_two);
            scanf("%d", &num);
            sem_post(&sem_one);
        }
        return NULL;
    }

    void * accu(void * arg)
    {
        int sum = 0 , i;
        for (i = 0; i < 5; i++) {
            sem_wait(&sem_one);
            sum+= num;
            sem_post(&sem_two);
        }
        printf("Result: %d \n", sum);
        return NULL;
    }

4. Implementation of multithreaded concurrent server

Posted by PierceCoding on Fri, 30 Aug 2019 18:16:32 -0700