什么是线程同步?
线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作。
pthread_create()
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);//用于创建新的线程
//*thread,指向线程标识符的指针,用于存储新线程的标识符
//attr,通常位NULL
//start_routine,指向线程主函数的指针,新线程将从这个函数开始执行
//arg,传递给 start_routine
的参数
//成功返回0,错误返回错误码
pthread_join()
int pthread_join(pthread_t thread, void **retval);//用于等待一个线程结束并回收其资源
//thread,要等待的线程的标识符
//retval,存储被等待线程的返回值的地址。如果不关心返回值,可以传递 NULL
//成功返回0,错误返回错误码
互斥锁
pthread_mutex_init()
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);//初始化互斥量
//mutex,指向要初始化的互斥锁的指针
//attr,指向线程属性对象的指针。如果传递 NULL
,则使用默认的属性
//成功返回0,错误返回错误码
pthread_mutex_lock()
int pthread_mutex_lock(pthread_mutex_t *mutex);//用于对指定的互斥量进行加锁
//mutex,指向互斥量的指针
//成功返回0,错误返回错误码
pthread_mutex_unlock()
int pthread_mutex_unlock(pthread_mutex_t *mutex); //用于解锁之前由 pthread_mutex_lock
函数锁定的互斥量
//mutex,指向互斥量的指针
//成功返回0,错误返回错误码
pthread_mutex_destroy()
int pthread_mutex_destroy(pthread_mutex_t *mutex); //用于销毁互斥锁
//mutex,指向待销毁的互斥锁的指针
//成功返回0,错误返回错误码
实践
主线程和函数线程模拟访问打印机,主线程输出第一个字符'a'表示开始使用打印机,输出第二个字符'a'表示结束使用,函数线程操作与主线程相同。
cpp
#include<iostream>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
//#include <semaphore.h>
using namespace std;
pthread_mutex_t mutex;
void *thread_fun(void *arg)
{
int i = 0;
for (; i < 5; i++)
{
pthread_mutex_lock(&mutex);
write(1, "B", 1);
// cout << "B";fflush(stdout);
sleep(rand() % 3); // 随机睡眠一段时间,用于模拟线程在做其他事情
write(1, "B", 1);
//cout << "B"; fflush(stdout);
pthread_mutex_unlock(&mutex);
sleep(rand() % 3);
}
pthread_exit(NULL);
}
int main()
{
pthread_t id;
pthread_mutex_init(&mutex, NULL);
pthread_create(&id, NULL, thread_fun, NULL);
int i = 0;
for (; i < 5; i++)
{
pthread_mutex_lock(&mutex);
write(1, "A", 1);
//cout << "A"; fflush(stdout);
sleep(rand() % 3);
write(1, "A", 1);
//cout << "A"; fflush(stdout);
pthread_mutex_unlock(&mutex);
sleep(rand() % 3);
}
pthread_join(id, NULL);
pthread_mutex_destroy(&mutex);
cout << endl;
exit(0);
}
输出结果:AABBAABBAABBAABBAABB
信号量
sem_init()
int sem_init(sem_t *sem, int pshared, unsigned int value);//初始化信号量
//sem,指向要初始化的信号量的指针
//value,信号量的初始值
sem_wait()
int sem_wait(sem_t *sem);//等待信号量
//sem,指向要初始化的信号量的指针
sem_post()
int sem_post(sem_t *sem);//释放信号量
//sem,指向要初始化的信号量的指针
sem_destroy()
int sem_destroy(sem_t *sem);//销毁信号量
//sem,指向要初始化的信号量的指针
实践
实现一个基于线程和信号量的简单的生产者-消费者模型,主线程用于从用户输入中获取数据,子线程读取数据并将其写入文件。
cpp
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <fcntl.h>
using namespace std;
char buff[128] = {0};
sem_t sem1;
sem_t sem2;
void *PthreadFun(void *arg)
{
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
if (fd == -1)
{
cout << "open failed" << endl;
}
// 函数线程完成将用户输入的数据存储到文件中
while (1)
{
sem_wait(&sem2);
if (strncmp(buff, "end", 3) == 0)
{
break;
}
write(fd, buff, strlen(buff));
memset(buff, 0, 128);
sem_post(&sem1);
}
sem_destroy(&sem1);
sem_destroy(&sem2);
return NULL;
}
int main()
{
sem_init(&sem1, 0, 1);
sem_init(&sem2, 0, 0);
pthread_t id;
int res = pthread_create(&id, NULL, PthreadFun, NULL);
// 主线程完成获取用户数据的数据,并存储在全局数组 buff 中
while (1)
{
sem_wait(&sem1);
cout << "please input data: " << endl;
//fgets(buff, 128, stdin);
cin >> buff;
//buff[strlen(buff) - 1] = 0;
buff[strlen(buff)] = 0;
sem_post(&sem2);
if (strncmp(buff, "end", 3) == 0)
{
break;
}
}
return 0;
}
条件变量
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待
这个共享数据的线程。
pthread_cond_init()
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);//初始化条件变量
//cond,指向要初始化的条件变量的指针
//attr,通常为NULL
pthread_cond_wait()
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);//在满足某个条件之前让线程进入等待状态
//cond,指向条件变量的指针
//mutex,指向互斥锁的指针
pthread_cond_signal()
int pthread_cond_signal(pthread_cond_t *cond);//向等待在条件变量上的单个线程发送信号
//cond,指向条件变量的指针
pthread_cond_broadcast()
int pthread_cond_broadcast(pthread_cond_t *cond); //向等待在条件变量上的所有线程发送信号
//cond,指向条件变量的指针
pthread_cond_destroy()
int pthread_cond_destroy(pthread_cond_t *cond);//销毁条件变量
//cond,指向条件变量的指针
实践
使用条件变量和互斥锁使两个线程交替输出信息。
cpp
#include<iostream>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
using namespace std;
pthread_mutex_t mutex;
pthread_cond_t cond;
void *fun1(void *arg)
{
char *s = (char *)arg;
while (1)
{
// 阻塞,被唤醒
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
cout << "fun1 read:" << s << endl;
if (strncmp(s, "end", 3) == 0)
{
break;
}
}
return NULL;
}
void *fun2(void *arg)
{
char *s = (char *)arg;
while (1)
{
// 阻塞,被唤醒
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
cout << "fun2 read:" << s << endl;
if (strncmp(s, "end", 3) == 0)
{
break;
}
}
return NULL;
}
int main()
{
pthread_t id[2];
char buff[128] = {0};
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_create(&id[0], NULL, fun1, (void *)buff);
pthread_create(&id[1], NULL, fun2, (void *)buff);
while (1)
{
cin >> buff;
if (strncmp(buff, "end", 3) == 0)
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
break;
}
else
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_join(id[0], NULL);
pthread_join(id[1], NULL);
pthread_cond_destroy(&cond);
return 0;
}