C/C++ Concurrency and Parallelism
Concurrency and Parallelism
Parallelism ∈ Concurrency
Concurrency: two or more actions(workflows) exists at the same time
Parallelism: two or more actions(workflows) is running at the same time
Why Multithreading?
- to achive Concurrency
- maximize (hardware)resource usage
Process vs Thread vs Coroutine
- Process:
- is a program
- is a unit for resource distribution
- process has at least one thread, each process has isolated resources, such as address space.
- heavyWeight, it takes more to create a process
- Thread:
- is a workflow, a subset of process
- is a unit for schedule and program executing.
- threads within a process share the same fate(false) and resources: memory(address space, stack), file descriptors, and other process related attributes.
- unique resources: stack, stack & stack pointer, program counter(
%rip
: the address of the next instruction)
- Coroutine(fiber, lightweight thread)
- used for asynchronous IO or generator
- a context switch technique in user mode
Kernel Thread vs Kernel-level thread(LWP) vs User-level thread
- kernel thread
- only run on kernel
- used for asynchronous operation(IO)
- kernel-level thread (LWP 轻量进程, 或者叫内核支持的线程)
- created by
clone()
systemcall
- created by
- user-level thread
- manage thread (creation,sechduling,destory) under user mode
Context Switch
- process switch
- the memory address space: memory addresses, mappings, page tables, and kernel resources, TLB and even L1 cache(some ARM processor)
- processor state
- thread switch
- processor state (such as the program counter and register contents) is generally very efficient.
Linux Process Communication
- pipe
- Semophore
- Signal
- Message Queue
- Share memory
- Socket, could cross-machine
Mutex vs Condition Variable vs semaphore vs CAS
Mutual Lock
- used for resource protection, the operation of this resource must be serial
- emphasize resource ownership, only owner can release the lock
- Mutex sleep-wait
- call
system_wait()
to start a context switch
- call
- Spin Lock busy-waiting
- take more user time to get lock as early as possible
Semaphore
- mainly used for schedule
- stands for the number of resource
- binary semaphore is simliar to mutex, but **semaphore does not record owner
- atomic operations: P(+1),V(-1)
- e.g. record the number of items of producer-consumer pro
Conditional variable:
- optmize such situation: lock -> check condition -> unlock
- to avoid frequent checking before condition satisfied
- it requires another thread to wake up when locked
other
- critical section
- a small code section only allows at most one thread access it at any moment.
- is more efficient
- read-write lock
- for high frequency reading and low frequency writing
- recursive mutex
- thread could lock its own clocked mutex repeatedly
- critical section
deadlock
necessary condition
- Mutual Exclusion (互斥):
at least one resource can be used by only one process at a time. - Hold and Wait (持有且等待):
There must exist a thread which holds some thread and waits for another resource held by some other thread. - No preemption (无法强制取得):
Once the resource has been allocated to the process, it can not be preempted(强制取得). - Circular wait (循环等待):
All the threads must wait for the resource in a cyclic manner where the last process waits for the resource held by the first thread.
detect deadlock
- gdb
- kernel tracing: Valgrind, Lockdep, bcc(Linux BPF-based)
producer-comsumer problem
mutex + cond var (linked list)
- mutex + cond var
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47struct msg {
struct msg *next;
int num;
};
struct msg *head; //queue header pointer
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; //cond var
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //mutex
void *consumer(void *p)
{
struct msg *mp;
for(;;) {
pthread_mutex_lock(&lock); //互斥量保护条件变量
while(head == NULL) {
pthread_cond_wait(&has_product, &lock);
}
mp = head;
head = mp->next;
pthread_mutex_unlock(&lock);
printf("Consume %d\n", mp->num);
free(mp);
sleep(rand() % 5);
}
}
void *producer(void *p)
{
struct msg *mp;
for(;;) {
mp = malloc(sizeof(struct msg));
mp->num = rand() % 1000 + 1;
printf("Produce %d\n", mp->num);
pthread_mutex_lock(&lock);
mp->next = head;
head = mp; //条件变化,要加锁
pthread_mutex_unlock(&lock);
pthread_cond_signal(&has_product);
sleep(rand() % 5);
}
}
Semaphore (ring queue)
这里blank_number是空位的意思,product_number是队列中的元素个数
1 |
|
CAS (lock free)
1 | EnQueue(x) //进队列 |
ABA problem
1)一次用CAS检查双倍长度的值,前半部是指针,后半部分是一个计数器。
2)只有这两个都一样,才算通过检查,要吧赋新的值。并把计数器累加1。
1 | SafeRead(q) |
Reference
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Comment