多线程编程基础

来源:互联网 发布:c语言公式的编程案例 编辑:程序博客网 时间:2024/06/05 03:40

线程

值得参考:http://www.csc.villanova.edu/~mdamian/threads/posixthreads.html

线程控制

创建线程

#include <pthread.h>int pthread_create(thread_id, attr, func, arg)

其中func表示该线程需要执行的代码地址

终止线程

从线程代码处return

pthread_cancle终止同一进程中的另一线程

调用pthread_exit来终止自己

设置取消线程的状态和类型

Pthread_setcancelstate() 有三种状态:

PTHREAD_CANCEL_ENABLE表示线程是可以取消的

PTHREAD_CANCEL_DISABLE 表示线程是不可取消的

PTHREAD_CANCEL_DEFERRED 表示线程是可以延迟取消的,就是收到取消请求后,会继续执行下去,直到找到下一个取消点

类似pthread_join,pthread_cond_wait都是取消点

 

PTHREAD_CANCEL_ASYNCHRONOUS表示异步取消方式,表示线程收到取消请求后,立即取消退出

默认情况下,一个线程是可取消的,并且是同步取消

 

如果一个线程正执行到一个锁内,已经获得锁了,这是如果它因为异常退出了,那么此时就是一个死锁的问题

线程在结束的时候,会自动执行一个cleanup函数句柄,这个函数句柄里有一个stack,通过pthread_cleanup_push往这个stack里压入函数,

退出的时候,cleanup函数,会自动从这些栈里拿出函数执行

 

如果函数没有异常退出,那这些栈内的函数如何处理?

通过pthread_cleanup_pop弹出这些函数

 

可使用pthread_join来挂起线程,等待终止

int pthread_join(threaded, value_ptr)

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL 。对一个尚未detach的线程调用pthread_join或pthread_detach都可以把该线程置为detach状态,也就是说,不能对同一线程调用两次pthread_join,或者如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

 

pthread_kill(id, sig)

发送信号到线程,若sig=0,是一个保留信号,用来检测线程是否存活

返回值ESRCH表示线程已经不存在了

返回值EINVAL表示非法的信号

返回其他值表示线程还存在

 

int pthread_detach(threadid)

 

在c++中如何使用pthread线程库

Entry()线程入口函数声明为static模式

 

C++中的std::thread类

 

封装thread为c++类

线程同步

条件变量

   条件变量是一种同步机制,允许线程挂起,直到共享数据上的某些条件得到满足。条件变量上的基本操作有:触发条件(当条件变为 true 时);等待条件,挂起线程直到其他线程触发条件。条件变量要和互斥量相联结,以避免出现条件竞争--一个线程预备等待一个条件变量,当它在真正进入等待之前,另一个线程恰好触发了该条件。

一般的编程方法:

pthread_mutex_lock(); //获取互斥量while(condition_is_false) //等待其他的线程修改条件,触发条件变量pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t * mutex); pthread_mutex_unlock(); //释放互斥量

以下的代码片段用来避免无限制的等待:

pthread_mutex_t count_lock; 
pthread_cond_t count_nonzero; unsigned count; void decrement_count()    { pthread_mutex_lock(&count_lock);      while (count == 0)         pthread_cond_wait(&count_nonzero, &count_lock);      count = count - 1;      pthread_mutex_unlock(&count_lock);    } void increment_count()    { pthread_mutex_lock(&count_lock);      if (count == 0)       pthread_cond_signal(&count_nonzero);      count = count + 1;//主要是这里才修改count参数,不能提前放锁     pthread_mutex_unlock(&count_lock);     }

下面的例子更是提醒程序员慎用pthread_cond_broadcast函数

#include <pthread.h>pthread_mutex_t rsrc_lock; pthread_cond_t rsrc_add;unsigned int resources; get_resources(int amount)   { pthread_mutex_lock(&rsrc_lock);    while (resources < amount)                       pthread_cond_wait(&rsrc_add, &rsrc_lock);            resources -= amount;    pthread_mutex_unlock(&rsrc_lock);   }    add_resources(int amount)   { pthread_mutex_lock(&rsrc_lock); //这里不加锁,可能会导致循环等待    resources += amount;    pthread_cond_broadcast(&rsrc_add);     pthread_mutex_unlock(&rsrc_lock);   }


这三个例子告诉我们,要确保不进入无限的等待,要在改变状态之前加锁,改变完成之后解锁。

RPC

Foolscap:针对某些特定场景下的应用,不必生搬硬套的使用http,或者实现另外一种RPC方法。若需要在双方都要使用方法调用,那么foolscap是一个不错的选择。

mutex

多线程程序中,解决访问冲突的最常用的方法是:互斥锁mutex(mutual exclusive lock),组成“读-修改-写”的原子操作;互斥锁保护的是lock之后所有的变量。

创建和初始化

pthread_mutex_tpthread_mutex_init(pthread_mutex_t *mutex, constpthread_mutexattr_t *attr)初始化mutex,使用属性attr,若attr为NULL,则表示使用默认属性。pthread_mutex_destroy(pthread_mutex_t *mutex)

加锁

int pthread_mutex_lock(pthread_mutex_t *mutex)int pthread_mutex_trylock(pthread_mutex_t *mutex)int pthread_mutex_unlock(pthread_mutex_t *mutex)

一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。

如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待


死锁

一个线程先后2次调用lock,第二次调用时,由于锁已被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用,该线程又因为挂起没有机会释放锁,因此就永远处于挂起等待状态,这是第一种常见的死锁;

第二种常见的死锁情况是:线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程AB都永远处于挂起状态了。不难想象,如果涉及到更多的线程和更多的锁,有没有可能死锁的问题将会变得复杂和难以判断。

则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用,以免死锁。

spin lock

http://www.cnblogs.com/diyunpeng/archive/2011/06/07/2074059.html

自旋锁

Busy waiting:对于单cpu来说,很是耗费资源;自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。

PV操作

PV操作及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。

    P
操作申请资源:
    
1S1
    
2)若S1后仍大于等于零,则进程继续执行;
    
3)若S1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。

    V
操作释放资源:
    
1S1
    
2)若相加结果大于零,则进程继续执行;
    
3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。


0 0