又一个新坑QAQ
Shared-Memory Programming
Definition: Processes communicate or work together with each other through a shared memory space which can be accessed by all processes.
Many issues as well: Synchronization, Deadlock, Cache coherence
Threads vs. Processes
一个Processes可以create出许多Threads。两者最大的差别是:**Thread之间有一部分memory contains是共用的,还有一部分是不共用的。**共用的部分就是Thread可以沟通的原因,而不共用的地方则是每个Thread可以独立执行code的原因。
名称 | 含义 |
---|---|
Process (heavyweight process) | complete separate program with its own variables, stack, heap, and everything else. |
Thread (lightweight process) | share the same memory space for global variables, resources. |
这里共用的resource举一个例子:Thread打开一个文件之后,会产生一个handler,其他的Thread也可以使用这个handler去执行程式,这就是resource的共用。
In Linux, Threads are created via clone a process with a flag to indicate the level of sharing.
也就是说,如果一个一个新的Thread与之前的process完全不共享内存,它就是一个新的process。
为什么使用Thread
-
Lower creation/management cost vs. Process.
-
Faster inter-process communication vs. MPI.(如果程式跑在一台电脑里,MPI速度就没有Pthread好了)
Pthread
什么是Pthread
POSIX (Potable Operating System Interface) standard is specified for portability across Unix-like systems.
Pthread is the implementation of POSIX standard for thread.
这里,thread中执行的程式不一定是一样的。
Pthread Creation
pthread_create(thread,attr,routine,arg)
/**
**********
thread: An unique identifier (token) for the new thread.main thread通过这个token与新创建的thread沟通。
attr: It is used to set thread attributes. NULL for the default values.
routine: The routine that the thread will execute once it is created.
arg: A single argument that may be passed to routine.
**********
**/
使用pthread_create()
来创建一个新的thread,新的thread执行routine指向的function call就可以。新创建的thread与之前的thread之间就可以被OS查询到,于是就可以在不同的core上执行。
#include <pthread.h>
#include <stdio.h> #define NUM_THREADS 5
void *PrintHello(void *threadId) {
long* data = static_cast <long*> threadId; printf("Hello World! It's me, thread #%ld!\n", *data);
pthread_exit(NULL); }
int main (int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
for(long tid=0; tid<NUM_THREADS; tid++){
} pthread_create(&threads[tid], NULL, PrintHello, (void *)&tid);
/* Last thing that main() should do */
pthread_exit(NULL); }
从上例中可以看到:
-
传递的参数
arg
,实际上传递的是一个void
类型的指针,在传递之后通过强制类型转换转换成原来的类型。 -
如果不给主进程回传消息的时候,可以使用
phtread_exit(NULL);
命令。
Pthread Joining & Detaching
一个线程最后会被 detach 或者被 join 。
pthread_join(threadId, status)
/**
**********
Blocks until the specified threadId thread terminates
One way to accomplish synchronization between threads
Example: to create a pthread barrier
for (int i=0; i<n; i++) pthread_join(thread[i], NULL);
**********
**/
pthread_join()
的作用是让call join的那个function block在pthread_join()
,直到threadId
对应的thread结束,才能继续。通常,这是为了取回status
中的值,但这也是一种实现同步的方式。
pthread_detach(threadId)
/**
**********
Once a thread is detached, it can never be joined
Detach a thread could free some system resources
**********
**/
pthread_detach(threadId)
的作用是将thread合并,但是不需要被合并的thread中的返回值或者被合并的thread中本身就没有返回值。比如,在web server中,每监听到一个request,就会创建一个新的thread,但main thread并不关心创建的thread执行了什么,因此也就不需要返回值。
Synchronization Problem & Tools
Synchronization Problem
The outcome of data content should NOT be decided by the execution order among processes.
Critical Section & Mutual Exclusion
-
Critical Section is a piece of code that can only be accessed by one process/thread at a time
-
Mutual exclusion is the problem to insure only one process/thread can be in a critical section
Locks
the simplest mechanism for ensuring mutual exclusion of critical section。
在Pthread里,Lock是mutex
,代表mutual exclusion
。
原理:
while (lock == 1);
lock = 1;
/**
...
critical section
...
**/
lock = 0;
/* no operation in while loop */ /* enter critical section */
/* leave critical section */
Pthread Lock/Mutex Routines
#include “pthread.h”
pthread_mutex_t mutex;
//mutex并不是一个简单的lock,而是一个结构体,因此需要init
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
/**
...
Critical Section
...
**/
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);
mutex一定要是个global variable,因为不同的thread都要访问mutex,从而实现同步。
Counded-Buffer Problem
Producer process produces information that is consumed by a Consumer process
Parbegin
Producer: begin
repeat
/**
produce an item in nextp;
**/
P(empty);
P(mutex);
add nextp to buffer;
V(mutex);
V(full);
until false;
end;
consumer:begin
repeat
P(full);
P(mutex);
remove an item from buffer to nextc
释放缓冲区
V(mutex);
V(empty);
/**
consume the item in nextc;
**/
until false;
end;
Parend;
Condition Variables (CV)
CV represent some condition that a thread can:
- Wait on, until the condition occurs;
- Notify other waiting threads that the condition has occurred .
Three operations on condition variables:
在Pthread中,CV的类型是pthread_cond_t
函数 | 作用 |
---|---|
pthread_cond_wait(&theCV,&someLock) | Block until another thread calls signal() or broadcast() on the CV |
pthread_cond_signal(&theCV) | Wake up one thread waiting on the CV |
pthread_cond_broadcast(&theCV) | Wake up all threads waiting on the CV |
只有在critical section
中,才能使用CV。
Using Condition Variable
/*
**********
Example:
A threads is designed to take action when x=0
Another thread is responsible for decrementing(递减) the counter
**********
*/
/*定义*/
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_cond_init (cond, NULL);
pthread_mutex_init (mutex, NULL);
/*实现*/
action() {
pthread_mutex_lock (&mutex);
if (x != 0)
pthread_cond_wait (cond, mutex);
pthread_mutex_unlock (&mutex);
take_action();
}
counter() {
pthread_mutex_lock (&mutex);
x--;
if (x==0)
pthread_cond_signal (cond);
pthread_mutex_unlock (&mutex);
}
All condition variable operation MUST be performed while a mutex is locked! But why is the lock necessary?
Because event counter “x” is a SHARED variable
- If no lock on thread action()…
Wait after any thread (i.e. not counter) sets “x” to 0 - If no lock on thread counter()…
No guarantee that decrement and test of “x” is atomic - Requiring CV operations to be done while holding a lock
prevents a lot of common programming mistakes
Semaphore
A tool to generalize the synchronization problem
有两个不同的操作:wait & signal
POSIX Semaphore
Semaphore is part of POSIX standard BUT it is not belonged to Pthread.
sem_init(sem_t *sem, int pshared, unsigned int value);
sem_wait(sem_t *sem);
sem_post(sem_t *sem);
sem_getvalue(sem_t *sem, int *valptr);
sem_destory(sem_t *sem);