Chapter 4 concurrent programming
This chapter mainly introduces the related contents of concurrent programming, including parallel computing, sequential algorithm and parallel algorithm, parallelism and concurrency; This paper explains the principle of thread and the advantages of machine over process. At the same time, it also carries out the actual operation of thread management and concurrent programming, so that we can have a deeper understanding of the principles and methods of multitasking, thread synchronization and concurrent programming.
4.1 parallel computing
(1) Background:
Early computers had only one processing component, called processor or central processing unit. Most programs performed serial computing, and the algorithm based on divide and conquer principle was highly parallel. Therefore, there was a parallel computing scheme that tried to use multiple processors executing parallel computing to solve the problem.
(2) Sequential algorithm and parallel algorithm
All steps of the sequential algorithm are executed in sequence through a single step. When all steps are completed, the algorithm ends.
In the parallel algorithm, the steps in the same code block are executed in parallel, and the steps of the next code block are executed after all are completed.
4.2 threads
(1) Thread principle
An operating system contains many concurrent processes. In the process model, a process is an independent execution unit. All processes are performed in kernel mode and user mode.
Kernel mode: each process executes in a unique address space, which is separate from other processes. Multiple independent processes share the same execution path. When a process must wait for an event, the whole process will stop executing.
A thread is an independent execution unit of a process in the same address space. To create a process is to create a main thread in a unique address space.
(2) Thread advantages
1. Faster thread creation and switching
2. The response speed of the thread is faster
3. Threads are more suitable for parallel computing
(3) Thread disadvantages
1. Due to address space sharing, the county needs explicit synchronization from users
2. Many library functions may be thread unsafe
3. On a single CPU system, using threads to solve problems is actually slower than using sequential programs.
4.3 thread operation
The execution trajectory of a thread is similar to that of a process. Users can do this in kernel mode or user mode
In user mode, threads execute in the same address space of the process, but each thread has its own execution stack. Thread is an independent execution unit. It can make system calls to the kernel according to the kernel scheduling policy of the operating system. It becomes suspended, activated and continued execution.
4.4 thread management function
1. Creating threads using pthread_ The create() function creates a thread
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
2. The thread ID uses pthread_ The equal() function compares thread IDs
int pthread_equal(pthread_t t1,pthread_t t2); Different threads return 0, otherwise non-0 `
3. Thread termination a thread can call a function to terminate
void pthread_exit(void *status); 0 Exit value indicates normal termination, and non-zero value indicates abnormal termination
4. Thread connection a thread can wait for the termination of another thread and terminate the exit state of the thread through the function.
int pthread_join (pthread_t thread, void **status ptr); `
4.5 thread synchronization
When multiple threads try to modify a shared variable or data structure at the same address, if the modification result depends on the execution order of the threads, it is called a static condition.
(1) Mutex
The simplest synchronization tool is a lock, which allows the executing entity to continue execution only if there is a lock. In Pthread, locks are called mutexes. They must be initialized before use.
There are two ways to initialize mutex addresses:
Static method: pthreaa—mutex_t m = PTHREAD_MUTEX_INITIALIZER; Define mutex m, And initialize it with default properties. Dynamic methods: Using pthread_ mutex _init() Function, available through attr Parameter sets the mutually exclusive property.
(2) Deadlock prevention
Mutex uses blocking protocol. If a thread cannot get the mutex, it will be blocked and continue after the mutex is unlocked. In any blocking protocol, misuse of locking may cause some problems. The most common and prominent problem is deadlock.
There are many methods to solve possible deadlock problems, including deadlock prevention, deadlock avoidance, deadlock detection and recovery.
In the actual system, the only feasible method is deadlock prevention, trying to prevent deadlock when designing parallel algorithms. A simple deadlock prevention method is to sort mutexes and ensure that each thread requests mutexes in only one direction, so that there will be no loops in the request sequence.
(3) Conditional variable
As a lock, mutex is only used to ensure that threads can only access shared data objects in critical areas. Conditional variables provide a way for threads to collaborate.
In Pthread, use the type pthread_cond_t to declare condition variables, and must be initialized before use. Condition variables can be initialized in two ways:
Static method :When declared, define a condition variable con,And initialize it with default properties, such as: pthread_cond_t con = PTHREAD_COND_INITALLIZER; Dynamic method :use pthread_cond_init()Functions, by attr Parameter sets the condition variable.
(4) Semaphore
The main difference between semaphores and conditional variables is that the former includes a counter, operability counter, and tests the counter value to make decisions, while the latter requires a specific mutex to execute the critical area.
5.1 practice
According to example 4.2 in the book, the final code is as follows:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> typedef struct{ int upperbound; int lowerbound; }PARM; #define N 10 int a[N]={5,1,6,4,7,2,9,8,0,3};// unsorted data int print(){//print current a[] contents int i; printf("["); for(i=0;i<N;i++) printf("%d ",a[i]); printf("]\n"); } void *Qsort(void *aptr){ PARM *ap, aleft, aright; int pivot, pivotIndex,left, right,temp; int upperbound,lowerbound; pthread_t me,leftThread,rightThread; me = pthread_self(); ap =(PARM *)aptr; upperbound = ap->upperbound; lowerbound = ap->lowerbound; pivot = a[upperbound];//pick low pivot value left = lowerbound - 1;//scan index from left side right = upperbound;//scan index from right side if(lowerbound >= upperbound) pthread_exit (NULL); while(left < right){//partition loop do{left++;} while (a[left] < pivot); do{right--;}while(a[right]>pivot); if (left < right ) { temp = a[left];a[left]=a[right];a[right] = temp; } } print(); pivotIndex = left;//put pivot back temp = a[pivotIndex] ; a[pivotIndex] = pivot; a[upperbound] = temp; //start the "recursive threads" aleft.upperbound = pivotIndex - 1; aleft.lowerbound = lowerbound; aright.upperbound = upperbound; aright.lowerbound = pivotIndex + 1; printf("%lu: create left and right threadsln", me) ; pthread_create(&leftThread,NULL,Qsort,(void * )&aleft); pthread_create(&rightThread,NULL,Qsort,(void *)&aright); //wait for left and right threads to finish pthread_join(leftThread,NULL); pthread_join(rightThread, NULL); printf("%lu: joined with left & right threads\n",me); } int main(int argc, char *argv[]){ PARM arg; int i, *array; pthread_t me,thread; me = pthread_self( ); printf("main %lu: unsorted array = ", me); print( ) ; arg.upperbound = N-1; arg. lowerbound = 0 ; printf("main %lu create a thread to do QS\n" , me); pthread_create(&thread,NULL,Qsort,(void * ) &arg);//wait for Qs thread to finish pthread_join(thread,NULL); printf ("main %lu sorted array = ", me); print ("20191303jcy\n") ; }
The practical screenshot is as follows:
5.2 problems and Solutions
An error is reported during the specific operation, as shown in screenshot 1. After asking the students, it is judged that there is a problem with the compilation. Since the POSIX standard of threads is involved, it is necessary to use Pthread command line tool in Unix operating system
gcc -pthread
Command to compile, and the final result is shown in Figure 2 above