OSTEP:27 Thread Api

POSIX互斥锁

使用锁之前先初始化

#include <pthread.h>

静态直接使用宏初始化pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
动态使用初始化函数
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

pthread_mutex_init(mutex,NULL)和用PTHREAD_MUTEX_INITIALIZER宏效果一样

attr属性

int pthread_mutexattr_gettype(pthread_mutexattr_t *attr,int *kind);得到后

int pthread_mutexattr_settype(pthread_mutexattr_t *attr,int kind); ;来设置

  • PTHREAD_MUTEX_NORMAL:普通类型互斥锁,不提供死锁检测或者错误检查。

  • PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平

  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。

  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保证当不允许多次加锁时不出现最简单情况下的死锁。

  • 进程间共享(pshared):可以设置为 PTHREAD_PROCESS_PRIVATEPTHREAD_PROCESS_SHARED。默认为 PTHREAD_PROCESS_PRIVATE,表示仅在同一进程内共享。

成功返回0,失败非0

加锁 解锁

加锁,互斥锁加锁状态线程进入等待(阻塞)状态,直到互斥锁释放

int pthread_mutex_lock(pthread_mutex_t* mutex);

加锁 互斥锁加锁状态线程直接返回非0

int pthread_mutex_trylock(pthread_mutex_t* mutex);

加锁,阻塞tsptr指定时间的thread_mutex_lock,超时或获取锁后返回

#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t mutex, const struct timespec *tsptr);
/*
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒
};
*/
使用时可以
struct timespec time_out;
clock_gettime(CLOCK_REALTIME, &time_out);
time_out.tv_sec += seconds;
pthread_mutex_timedlock(mutex,&time_out)

解锁

int pthread_mutex_unlock(pthread_mutex_t* mutex);

成功返回0,失败非0

销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

要求锁当前初始化过且处于开放状态

成功返回0,失败非0

#include <pthread.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int sum = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//让12都看见锁
void* Sum(void*) {
for (size_t i = 0; i < 100000; i++) {
assert(0 == pthread_mutex_lock(&mutex));
sum++;
assert(0 == pthread_mutex_unlock(&mutex));
}
}
int main() {
pthread_mutex_t x;
pthread_t pid1, pid2;
pthread_create(&pid1, NULL, Sum, NULL);
pthread_create(&pid2, NULL, Sum, NULL);
pthread_join(pid1,NULL);
pthread_join(pid2,NULL);
pthread_mutex_destroy(&mutex);
printf("%d",sum);
}

条件变量

如果一个线程在等待另一个线程继续执行某些操作,条件变量就很有用,或者说是利用线程间共享的全局变量进行同步的机制,和互斥锁一起使用,避免竞态条件

条件变量初始化

和锁差不多

pthread_cond_t cond=PTHREAD_COND_INITIALIZER

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

睡眠 唤醒

>>>睡眠

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

阻塞当前线程,并把互斥锁解开,被唤醒后,返回前会再次获得互斥锁上锁

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t*mutex, const struct timespec *abstime);

比pthread_cond_wait多了限定的等待时间,abstime内没由被唤醒,上锁,结束等待,返回ETIMEOUT

>>>唤醒

int pthread_cond_signal(pthread_cond_t *cond);

使在条件变量上等待的线程中的一个线程重新开始,优先级高等待时间长的线程先开始

int pthread_cond_broadcast(pthread_cond_t *cond);

唤醒所有条件变量上等待的线程

销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond)

使用前必须没有在该条件变量上等待的线程

#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
bool flag = false;
void* thread_func(void*) {
while(1) {
assert(pthread_mutex_lock(&lock) == 0);
while (flag == false) {//这里为什么要用while而不是if? POSIX标准要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,
//有一些 pthread实现可能会错误地唤醒等待的线程,加个while即使被错误唤醒,while条件不满足,继续等待
//条件变量一个简单规则就是总是使用while来避免一些错误,while了就是得劲😂
pthread_cond_wait(&cond, &lock);
puts("grxer");
}
assert(pthread_mutex_unlock(&lock) == 0);
}
}
int main() {
pthread_t pid;
pthread_create(&pid, NULL, thread_func, NULL);
sleep(1);//防止flag = true;执行比while (flag == false)早
assert(pthread_mutex_lock(&lock) == 0);
flag = true;//操作flag临界量加个锁
pthread_cond_signal(&cond);
assert(pthread_mutex_unlock(&lock) == 0);
sleep(1);
flag = false;
assert(pthread_mutex_lock(&lock) == 0);
flag = true;//操作flag临界量加个锁
pthread_cond_signal(&cond);
assert(pthread_mutex_unlock(&lock) == 0);
pthread_join(pid, NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
puts("end");

}