要引入线程控制这个概念是由于在多线程中存在资源竞争的问题,导致结果与想的不一样。
比如这个程序,想实现的A加到10000去。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int A = 0 ;
void * th(void* arg)
{
int i = 5000;
while(i--)
{
int tmp = A;
printf("A is %d\n",tmp+1);
A = tmp+1;
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,th,NULL);
pthread_create(&tid2,NULL,th,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
运行结果却是这样的
所以要引入线程控制的互斥与同步量;
一、线程控制:
互斥锁:在多线程中对临界资源的排他性访问。特点:谁上锁谁解锁
线程控制(互斥)的执行步骤:定义互斥锁、初始化锁、加锁、解锁、销毁。
1、定义互斥锁:pthread_mutex_t mutex;
2、初始化锁:初始化互斥锁,使其处于可用状态。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
参数:
mutex:要初始化的互斥锁。
attr:互斥锁的属性设置,通常传递 NULL 使用默认属性。
返回值:0成功,非零失败
3、加锁:锁定互斥锁,以保护共享资源。其他线程在该锁被释放之前不能访问被保护的代码段。
int pthread_mutex_lock(pthread_mutex_t *mutex);
返回值:成功返回 0;失败返回非零值。
注意:如果互斥锁已经被其他线程加锁,调用 pthread_mutex_lock 的线程将被阻塞,直到锁被释放。避免在持有锁的线程中调用可能导致死锁的函数。
4、解锁:解锁互斥锁,使其他线程可以获取该锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回 0;失败返回非零值。
注意:每次加锁操作后必须对应一个解锁操作,以防止死锁和资源泄漏。
5、销毁:销毁互斥锁,释放相关的系统资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
返回值:成功返回 0;失败返回非零值。
注意:在销毁互斥锁之前,确保所有线程都已经释放了对该锁的持有,并且该互斥锁不再被使用。
6、trylock():尝试获取锁而不阻塞。如果锁已经被其他线程持有,则立即返回而不阻塞当前线程。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
返回值:成功返回 0,表示成功获取锁。失败返回非零值
用多线程程序设计一个火车票售票系统, 要求至少有两个售票窗口,每个售票窗口不能重复买票,将100张车票均匀的从两个窗口卖出即可。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int WIN =3;
pthread_mutex_t mutex;
void* th(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
if(WIN>0)
{
WIN--;
pthread_mutex_unlock(&mutex);
printf("get win\n");
sleep(rand()%5);
printf("relese win\n");
pthread_mutex_lock(&mutex);
WIN++;
pthread_mutex_unlock(&mutex);
break;
}
else
{
pthread_mutex_unlock(&mutex);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int i = 0 ;
pthread_mutex_init(&mutex,NULL);
pthread_t tid[10]={0};
for(i=0;i<10;i++)
{
pthread_create(&tid[i],NULL,th,NULL);
}
for(i=0;i<10;i++)
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mutex);
return 0;
}
产生死锁的原因主要是:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
2、信号量:有一定先后顺序的对资源的排他性访问。特点:上锁与解锁执行者不一样。
信号量的步骤:信号量的定义、信号量的初始化、信号量的PV操作、信号量的销毁。
1.信号量定义:sem_t sem;
2. 信号量的初始化:初始化一个信号量。
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem:要初始化的信号量。
pshared:如果为 0,信号量在线程间共享;如果不为 0,信号量在进程间共享。
value:信号量的初始值。通常用来设置信号量的计数值,例如 0 表示不可用,1 表示可用。
返回值:成功返回 0;失败返回 -1。
注意:信号量的初始值应根据具体应用场景设置。例如,如果你希望一个信号量用于控制对某个共享资源的互斥访问,可以将初始值设为 1,表示资源是可用的。
3. 信号量的 PV 操作
信号量的 PV 操作包括 P(Proberen,申请资源)和 V(Verhogen,释放资源)。在 POSIX 信号量中,P 操作对应 sem_wait,V 操作对应 sem_post。
int sem_wait(sem_t *sem);
功能:申请信号量资源。如果信号量的值大于 0,则资源被分配,信号量值减 1;如果信号量值为 0,线程会被阻塞,直到信号量有可用资源。
int sem_post(sem_t *sem);
功能:释放信号量资源,将信号量的值增加 1。如果有线程因信号量值为 0 而被阻塞,则会唤醒一个被阻塞的线程。
4. 信号量的销毁
int sem_destroy(sem_t *sem);
功能:销毁信号量,释放相关的系统资源。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem_WIN;
void* th(void* arg)
{
sem_wait(&sem_WIN);
printf("get win\n");
sleep(rand()%5);
printf("relese win\n");
sem_post(&sem_WIN);
return NULL;
}
void* th1(void* arg)
{
int i =10;
while(i--)
{
sem_wait(&sem_H);
printf("hello ");
fflush(stdout);
sem_post(&sem_W);
}
return NULL;
}
void* th2(void* arg)
{
int i =10;
while(i--)
{
sem_wait(&sem_W);
printf("world\n");
sleep(1);
sem_post(&sem_H);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1,tid2;
sem_init(&sem_H,0,1);
sem_init(&sem_W,0,0);
pthread_create(&tid1,NULL,th1,NULL);
pthread_create(&tid2,NULL,th2,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
sem_destroy(&sem_H);//先打印hello,再打印word
sem_destroy(&sem_W);
//用同步信号量实现10个去三个窗口办业务
int i = 0 ;
pthread_t tid[10]={0};
sem_init(&sem_WIN,0,3);
for(i=0;i<10;i++)
{
pthread_create(&tid[i],NULL,th,NULL);
}
for(i=0;i<10;i++)
{
pthread_join(tid[i],NULL);
}
sem_destroy(&sem_WIN);
return 0;
}