Makefile文件
bin=ticket
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)
$(bin):$(obj)
$(cc) -o $@ $^ -lpthread
%.o:%.cc
@echo "Comiling $< to $@"
$(cc) -c $< -std=c++17
.PHONY:clean
clean:
rm -f $(bin) $(obj)
.PHONY:test
test:
echo $(src)
echo $(obj)
代码:
#include <stdio.h>
#include <string>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
int ticket = 100;
void* routine(void* args)
{
char *id = (char*)args;
// std::string id = static_cast<const char*>(args);
while(1)
{
if(ticket > 0)
{
usleep(10000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
}
else
{
break;
}
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2 , t3, t4;
pthread_create(&t1, nullptr, routine, (void*)"thread 1");
pthread_create(&t2, nullptr, routine, (void*)"thread 2");
pthread_create(&t3, nullptr, routine, (void*)"thread 3");
pthread_create(&t4, nullptr, routine, (void*)"thread 4");
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
pthread_join(t3, nullptr);
pthread_join(t4, nullptr);
return 0;
}
if
语句判断条件为真以后,代码可以并发的切换到其他进程usleep
这个模拟夜漫长业务的过程这个漫长的业务过程中,可能有多个线程会进入该代码段--ticket
操作本身就不是一个原子操作取出ticket–部分的汇编代码
objdump -d a.out > test.objdump
152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax
600b34 <ticket>
153 400651: 83 e8 01 sub $0x1,%eax
154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip)
600b34 <ticket>
--
操作并不是原子操作而是对应三条汇编指令:
load
将共享变量体的从内存加载到寄存器update
更新寄存器里面的只执行复议操作要解决以上问题需要做到三点:
要做到这三点,本身是上就是需要一把锁,linux
上提供这把锁叫互斥量
互斥量的接口 初始化互斥量的两种方法
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
销毁互斥量 销毁互斥量需要注意:
PTHREAD_ MUTEX_ INITIALIZER
初始化的互斥量不需要销毁int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回o,失败返回错误号
调用pthread_ lock
时,可能会遇到以下情况:
改进上面的售票系统:
#include <stdio.h>
#include <string>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
int ticket = 100;
pthread_mutex_t mutex;
void* routine(void* args)
{
char *id = (char*)args;
// std::string id = static_cast<const char*>(args);
while(1)
{
pthread_mutex_lock(&mutex);
if(ticket > 0)
{
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread_mutex_unlock(&mutex);
}
else
{
pthread_mutex_unlock(&mutex);
break;
}
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2 , t3, t4;
pthread_create(&t1, nullptr, routine, (void*)"thread 1");
pthread_create(&t2, nullptr, routine, (void*)"thread 2");
pthread_create(&t3, nullptr, routine, (void*)"thread 3");
pthread_create(&t4, nullptr, routine, (void*)"thread 4");
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
pthread_join(t3, nullptr);
pthread_join(t4, nullptr);
pthread_mutex_destroy(&mutex);
return 0;
}
RAII风格的互斥锁,C++11也有,比如:
std : : mutex mtx;
std : : lock_guard guard ( mtx ) ;
初始化
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t*restrict attr);
参数: cond:要初始化的条件变量 attr: NULL
销毁:
int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满⾜
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后面详细解释
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
简单案例:
PTHREAD_COND/MUTEX_INITIALIZER
进行测试,对其他细节暂不追究pthread_cond_init/pthread_cond_destroy
的方式,方便后续进行封装#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* active(void* args)
{
std::string name = static_cast<const char*>(args);
while(true)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
std::cout<< name << "活动..." << std::endl;
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t t1, t2;
pthread_create(&t1, nullptr, active, (void*)"thread -1");
pthread_create(&t2, nullptr, active, (void*)"thread -2");
sleep(3);
while(true)
{
//对比测试
pthread_cond_signal(&cond);//唤醒一个线程
// pthread_cond_broadcast(&cond);//唤醒所有线程
sleep(1);
}
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}