线程和线程同步

来源:互联网 发布:js随机数不重复 编辑:程序博客网 时间:2024/05/22 18:10
gcc编译包含线程的源代码需要加上-lpthread开关。

线程的优点:需要多任务时,节省程序运行的时空间消耗。
线程的缺点:各线程之间共享数据,不安全。
一个进程中的各线程有独立的寄存器和栈,其他空间都共享,包括进程打开的文件和信号处理。

#include <pthread.h>
int pthread_create(pthread_t* restrict thread, const pthread_attr_t attr, void* startroutine(void), void* argv)
attr传入NULL表示使用默认属性。
void pthread_exit(void* retval)
终止线程。线程不能通过exit终止。如果一个线程调用了exit,整个进程将终止。
子线程默认为非分离状态,即受到主线程控制,子线程结束后不会释放资源,需要在主线程中用pthread_join(thread1, NULL)接手并释放。

同步机制
1.互斥量
类型:pthread_mutex_t
拥有加锁和未加锁两种状态。

PTHREAD_MUTEX_INITIALIZER
静态创建互斥量。
用法:pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restrict attr)
动态创建互斥量。
int pthread_mutex_lock(pthread_mutex_t * mutex)
给互斥量mutex加锁,若互斥量已经加锁,则阻塞该线程。
int pthread_mutex_trylock(pthread_mutex_t * mutex)
尝试给互斥量加锁,若不能加上,errno设置为EBUSY,并返回EBUSY;若能加上,加锁且返回0;错误返回其他值。
int pthread_mutex_unlock(pthread_mutex_t * mutex)
给互斥量解锁。成功返回0,失败返回-1。
int pthread_mutex_destroy(pthread_mutex_t *mutex)
消除互斥量。

死锁产生的原因:
  1. 系统资源不足时内核态线程优先级不够,导致抢占资源失败
  2. 同一线程对一个互斥量两次加锁
  3. 线程A对A互斥量加锁,B对B互斥量加锁,然后A对B互斥量加锁,B对A互斥量加锁,则两个线程产生死锁
产生死锁的四个必要条件:
  • 互斥条件:一个资源在同一时间只能被一个线程或进程占用。
  • 请求与保持条件:线程或进程在请求被阻塞的情况下保持已拥有的资源不释放。
  • 不剥夺条件:不能强行剥夺线程或进程的资源。
  • 循环等待条件:若干个线程或进程头尾相接地循环等待对方释放资源。
不满足以上四个条件中的任一个,则不会发生死锁。

2.读写锁
类型:pthread_rwlock_t
有三个状态:读锁、写锁、未加锁。
加了写锁后任何线程都不能再加锁,加了读锁后任何线程都可以对它加读锁,但不能加写锁。
适合读比较频繁、写较少的情况,例如数据库。

PTHREAD_RWLOCK_INITIALIZER
静态创建读写锁。用法与互斥量相似。
int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)
加读锁,要注意判断返回值,因为加读锁的次数可能有限制,因此可能失败。
int pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)
加写锁,无需判断返回值。
int pthread_rwlock_unlock(pthread_rwlock_t * rwlock)
解锁,读锁和写锁都解开。
int pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)
尝试加读锁。
int pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)
尝试加写锁。
int pthread_rwlock_destroy(pthread_rwlock_t * rwlock)
消除读写锁。

3.条件变量
类型:pthread_cond_t
与互斥量一起使用,在达到条件时唤醒被阻塞的线程。

PTHREAD_COND_INITIALIZER
静态创建条件变量,与互斥量相似。
int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cattr)
初始化条件变量。若cattr置为NULL,则创建缺省的条件变量,否则按cattr设定属性。
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
解锁给定的mutex,并使当前线程阻塞在cond上,直到被唤醒为止。
为了避免意外唤醒(假唤醒),一般将wait放在循环中,循环的终止条件就是唤醒线程的条件。
int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * timeout)
带有等待时间的wait函数,若时间到还未被唤醒,则返回ETIMEDOUT。
int pthread_cond_signal(pthread_cond_t * cond)
int pthread_cond_broadcast(pthread_cond_t *cond)
唤醒阻塞在这个条件变量上的线程。signal唤醒一个线程,broadcast则唤醒所有线程。
如果没有线程在等待,这两个函数的唤醒信号将不会被保留。

4.信号量
类型:sem_t
struct semid_ds{
      struct ipc-perm sem_perm; //许可权限
      time_t sem_otime; //最后一次操作时间
      time_t sem_ctime; //最后一次调用semctl时间
      unsigned long sem_nsems; //信号量个数
};
信号量计数器大于等于0时代表该信号量集中剩余的系统资源,小于0时代表等待使用该信号量集中资源的线程数。
信号量计数器的值只能通过PV操作来改变,P操作将当前进程由运行状态转为阻塞状态,V操作将一个被阻塞的进程唤醒。
PV操作是原子操作。

#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value)
初始化信号量,pshared参数决定该信号量是否能在进程间共享,Linux系统下一般取0;value参数决定信号量计数器的初始值。
int sem_wait(sem_t *sem)
若sem计数器不大于0,则阻塞当前线程;否则使sem减一并继续执行。
事实上,这个函数试图对信号量“加锁”(信号量小于等于0视为加锁状态),如果未能成功加锁,则不会返回到调用它的线程中,直到成功加锁或者被信号解除为止。这与互斥量的加锁函数原理相同。
int sem_trywait(sem_t *sem)
类似wait,但若不能加锁会立刻返回,不阻塞线程。
int sem_post(sem_t *sem)
将sem加一,并发出信号唤醒阻塞的线程,让它们前来接受资源。
int sem_getvalue(sem_t *sem)
得到sem的值。
int sem_destroy(sem_t *sem)
消除信号量。

原创粉丝点击