线程

来源:互联网 发布:刚开淘宝店怎么刷信誉 编辑:程序博客网 时间:2024/06/03 17:06

主要内容来自apue及网络

Consistency Synchronization Race Deadlock

线程(thread)是“进程”中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。
线程是操作系统能够进行运算调度的最小单位。

死锁(deadlock):是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象:死锁。
避免死锁
1)可以通过小心地控制互斥量加锁的顺序来避免死锁的发生。
2)可以先释放占有的锁,然后等一段时间再试。

http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/
Linux 上线程开发 API 的概要介绍

多线程开发在 Linux 平台上已经有成熟的 Pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作, 分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。

#include <stdio.h>#include <sys/types.h>#include <unistd.h>#include <pthread.h>pthread_t ntid;void PrintIDs(const char *s){pid_t     pid;pthread_t tid;pid = getpid();tid = pthread_self();printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,(unsigned long)tid, (unsigned long)tid);}void *ThreadFun(void *arg){PrintIDs("new thread: ");return ((void *)0);}int main(){int err = 0;err = pthread_create(&ntid, NULL, ThreadFun, NULL);if (err != 0) {printf("Can't create thread! error code: %d\n", err);return -1;}PrintIDs("main thread:");sleep(1);return 0;}
#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <pthread.h>struct foo {int a, b, c, d;};void PrintFoo(const char *str, const struct foo *fp){printf("%s", str);printf(" structure at 0x%lx\n", (unsigned long)fp);printf(" foo.a = %d\n", fp->a);printf(" foo.b = %d\n", fp->b);printf(" foo.c = %d\n", fp->c);printf(" foo.d = %d\n", fp->d);}void *ThreadFun1(void *arg){struct foo foo = {1, 2, 3, 4};PrintFoo("thread 1:\n", &foo);pthread_exit((void *)&foo);}void *ThreadFun2(void * arg){printf("thread 2: ID is %lu\n", (unsigned long)pthread_self());pthread_exit((void *)0);}int main(){int err;pthread_t tid1, tid2;struct foo *fp;err = pthread_create(&tid1, NULL, ThreadFun1, NULL);if (err != 0) {printf("Can't create thread 1. Error code: %d\n", err);return -1;}err = pthread_join(tid1, (void *)&fp);if (err != 0) {printf("Can't join with thread 1. Error code: %d\n", err);return -1;}sleep(1);printf("Parent starting second thread\n");err = pthread_create(&tid2, NULL, ThreadFun2, NULL);if (err != 0) {printf("Can't create thread 2. Error code: %d\n"a, err);return -1;}sleep(1);PrintFoo("parent:\n", fp);return 0;}

The increment operation is usually broken down into three steps.
1. Read the memory location into a register.
2. Increment the value in the register.
3. Write the new value back to the memory location.
If the modification is atomic, then there isn’t a race.
If our data always appears to be sequentially consistent, then we need no additional synchronization. Our operations are sequentially consistent when multiple threads can’t observe inconsistencies in our data.

Multithreaded software design involves these types of trade-offs(权衡). If your locking granularity is too coarse, you end up with too many threads blocking behind the same locks, with little improvement possible from concurrency. If your locking granularity is too fine, then you suffer bad performance from excess locking overhead, and you end up with complex code.As a programmer, you need to find the correct balance between code complexity and performance, while still satisfying your locking requirements.

常用的同步机制
(1)Mutexes
A mutex(mutual-exclusion) is basically a lock that we set (lock) before accessing a shared resource and release (unlock) when we’re done. 
互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量经行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到有线程释放该互斥量。

#include <stdio.h>#include <unistd.h>#include <pthread.h>struct foo {pthread_mutex_t lock;int             count;int             id;/* more stuff here */};/* allocate the object */struct foo *FooAlloc(int id){struct foo *fp = NULL;if ((fp = malloc(sizeof(struct foo))) != NULL) {fp->count = 1;fp->id = id;if (pthread_mutex_init(&fp->lock, NULL) != 0) {free(fp);return NULL;}/* continue initialization */}return fp;}/* add a reference to the object */void FooHold(struct foo *fp){pthread_mutex_lock(&fp->lock);fp->count++;pthread_mutex_unlock(&fp->lock);}/* release a reference to the object */void FooRelease(struct foo *fp){pthread_mutex_lock(&fp->lock);if (--fp->count == 0) {  /* last reference */pthread_mutex_unlock(&fp->lock);pthread_mutex_destroy(&fp->lock);free(fp);} else {pthread_mutex_unlock(&fp->lock);}}
#include <stdio.h>#include <stdlib.h>#include <pthread.h>#define NHASH 29#define HASH(id) (((unsigned long)id)%NHASH)struct foo *fh[NHASH];pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;struct foo {pthread_mutex_t lock;int             count;int             id;struct foo      *next; /* protected by hashlock *//* more stuff here */};/* allocate the object */struct foo *FooAlloc(int id){struct foo *fp;int        idx;if ((fp = malloc(sizeof(struct foo))) != NULL) {fp->count = 1;fp->id = id;if (pthread_mutex_init(&fp->lock, NULL) != 0) {free(fp);return NULL;}idx = HASH(id);pthread_mutex_lock(&hashlock);fp->next = fh[idx];fh[idx] = fp;pthread_mutex_lock(&fp->lock);pthread_mutex_unlock(&hashlock);/* continue initialization */pthread_mutex_unlock(&fp->lock);}return fp;}/* add a reference to the object*/void FooHold(struct foo *fp){pthread_mutex_lock(&fp->lock);fp->count++;pthread_mutex_unlock(&fp->lock);}/* find an existing object */struct foo *FooFind(int id){struct foo *fp;pthread_mutex_lock(&hashlock);for (fp = fh[HASH(id)]; fp != NULL; fp = fp->next) {if (fp->id == id) {FooHold(fp);break;}}pthread_mutex_unlock(&hashlock);return fp;}/* release a reference to the object */void FooRelease(struct foo *fp){struct foo *tfp;int        idx;pthread_mutex_lock(&fp->lock);if (fp->count == 1) { /* last reference */pthread_mutex_unlock(&fp->lock);pthread_mutex_lock(&hashlock);pthread_mutex_lock(&fp->lock);/* need to recheck the condition */if (fp->count != 1) {fp->count--;pthread_mutex_unlock(&fp->lock);pthread_mutex_unlock(&hashlock);return;}/* remove from list */idx = HASH(fp->id);tfp = fh[idx];if (tfp == fp) {fh[idx] = fp->next;} else {while (tfp->next != fp) {tfp = tfp->next;}tfp->next = fp->next;}pthread_mutex_unlock(&hashlock);pthread_mutex_unlock(&fp->lock);pthread_mutex_destroy(&fp->lock);return fp;} else {fp->count--;pthread_mutex_unlock(&fp->lock);}}
#include <stdio.h>#include <stdlib.h>#include <pthread.h>#define NHASH 29#define HASH(id) (((unsigned long)id)%NHASH)struct foo *fh[NHASH];pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;struct foo {pthread_mutex_t lock;int             count; /* protected by hashlock */int             id;struct foo      *next; /* protected by hashlock *//* more stuff here */};/* allocate the object */struct foo *FooAlloc(int id){struct foo *fp;int        idx;if ((fp = malloc(sizeof(struct foo))) != NULL) {fp->count = 1;fp->id = id;if (pthread_mutex_init(&fp->lock, NULL) != 0) {free(fp);return NULL;}idx = HASH(id);pthread_mutex_lock(&hashlock);fp->next = fh[idx];fh[idx] = fp;pthread_mutex_lock(&fp->lock);pthread_mutex_unlock(&hashlock);/* continue initialization */pthread_mutex_unlock(&fp->lock);}return fp;}/* add a reference to the object*/void FooHold(struct foo *fp){pthread_mutex_lock(&hashlock);fp->count++;pthread_mutex_unlock(&hashlock);}/* find an existing object */struct foo *FooFind(int id){struct foo *fp;pthread_mutex_lock(&hashlock);for (fp = fh[HASH(id)]; fp != NULL; fp = fp->next) {if (fp->id == id) {fp->count++;break;}}pthread_mutex_unlock(&hashlock);return fp;}/* release a reference to the object */void FooRelease(struct foo *fp){struct foo *tfp;int        idx;pthread_mutex_lock(&hashlock);if (--fp->count == 0) { /* last reference, remove from list */idx = HASH(fp->id);tfp = fh[idx];if (tfp == fp) {fh[idx] = fp->next;} else {while (tfp->next != fp) {tfp = tfp->next;}tfp->next = fp->next;}pthread_mutex_unlock(&hashlock);pthread_mutex_destroy(&fp->lock);return fp;} else {pthread_mutex_unlock(&fp->lock);}}
(2)Reader–Writer Locks 
Reader–writer locks are similar to mutexes, except that they allow for higher degrees of parallelism. With a mutex, the state is either locked or unlocked, and only one thread can lock it at a time. Three states are possible with a reader–writer lock: locked in read mode, locked in write mode, and unlocked. Only one thread at a time can hold a reader–writer lock in write mode, but multiple threads can hold a reader–writer lock in read mode at the same time.
Reader–writer locks are also called shared–exclusive locks.
读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。
一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。
#include <stdlib.h>#include <pthread.h>struct job {struct job *next;struct jbo *prev;pthread_t  id;    /* tells which thread handles this job *//* more stuff here */};struct queue {struct job      *head;struct job      *tail;phread_rwlock_t lock;};/* Initialize a queue */int QueueInit(struct queue *qp){int err;qp->head = NULL:qp->tail = NULL;err = pthead_rwlock_init(&qp->lock, NULL);if (err != 0) {return err;}/* continue initialization */return 0;}/* Insert a job at the head of the queue */void JobInsert(struct queue *qp, struct job *jp){pthread_rwlock_wrlock(&qp->lock);jp->next = qp->head;jp->prev = NULL;if (qp->head != NULL) {qp->head->prev = jp;} else {qp->tail = jp;  /* list was empty */}qp->head = jp;pthread_rwlock_unlock(&qp->lock);}/* Append a job on the tail of the queue */void JobAppend(struct queue *qp, struct job *jp){pthread_relock_wrlock(&qp->lock);jp->next = NULL;jp->prev = qp->tail;if (qp->tail != NULL) {qp->tail->next = jp;} else {qp->head = jp;  /* list was empty */}qp->tail = jp;pthread_rwlock_unlock(&qp->lock);}/* Remove the given job from q queue */void JobRemove(struct queue *qp, struct job *jp){pthread_rwlock_wrlock(&qp->lock);if (jp == qp->head) {qp->head = jp->next;if (qp->tail == jp) {qp->tail = NULL;} else {jp->next->pvev = jp->prev;}} else if (jp == qp->tail) {qp->tail = jp->prev;jp->prev->next = jp->next;} else {jp->prve->next = jp->next;jp->next->prev = jp->prev;}pthread_rwlock_unlock(&qp->lock);}/* Find a job for the given thread ID */struct job *JobFind(struct queue *qp, pthread_t id){struct job *jp;if (pthread_rwlock_rdlock(&qp->lock) != 0) {return NULL;}for (jp = qp->head; jp != NULL; jp = jp->next) {if (pthread_equal(jp->id, id)) {break;}}pthread_rwlock_unlock(&qp->lock);return jp;}
(3)Condition Variables
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
#include <pthread.h>struct msg {struct msg *next;/* more stuff here */};struct msg *workq;pthread_cond_t qready = PTHREAD_COND_INITIALIZER;pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;void ProcessMsg(void){struct msg *mp;for (;;) {pthread_mutex_lock(&qlock);while (workq == NULL) {pthread_cond_wait(&qready, &qlock);}mp = workq;workq = mp->next;pthread_mutex_unlock(&qlock);/* now process the message mp */}}void EnqueueMsg(struct msg *mp){pthread_mutex_lock(&qlock);mp->next = workq;pthread_mutex_unlock(&qlock);pthread_cond_signal(&qready);}

(4)Spin Locks
A spin lock is like a mutex, except that instead of blocking a process by sleeping, the process is blocked by busy-waiting (spinning) until the lock can be acquired. A spin lock could be used in situations where locks are held for short periods of times and threads don’t want to incur the cost of being descheduled.


(5)Barriers
Barriers are a synchronization mechanism that can be used to coordinate multiple threads working in parallel. A barrier allows each thread to wait until all cooperating threads have reached the same point, and then continue executing from there. 




0 0
原创粉丝点击