C++之多线程编程互斥锁
在线程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
可以保证以下三点:
1.原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量。
2.唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量。
3.非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
从以上三点,我们看出可以用互斥量来保证对变量(关键的代码段)的排他性访问
创建C++控制台程序,代码如下
#include "stdafx.h"
#include <iostream>
#include <pthread.h>
//多线程头文件,可移植众多平台, pthread头文件和库需要自己下载,
//下载地址 https://sourceware.org/pthreads-win32/#download
//进入下载网站找到相应的 .exe下载即可,各种版本均可
#define NUM_THREADS 5 //进程数
int sum = 0; //定义全局变量,让所有线程同时写,这样就需要锁机制
pthread_mutex_t sum_mutex; //互斥锁
using namespace std;
//互斥锁的实现
class hhh
{
public:
//如何在线程函数中传入参数
static void * sayHello( void * args)
{
//对传入的参数进行强制类型转换,由无类型指针转变为整形指针,再用*读取其指向到内容
std:: cout<< "hello "<< *((int *)args) <<endl;
//先加锁,在修改sum的值,锁被占用就阻塞,知道拿到锁在修改sum
pthread_mutex_lock(&sum_mutex);
std::cout<<"before sum is : "<<sum<< " in thread" << *((int*)args) <<endl;
sum += *((int *)args);
std::cout<<"after sum is : "<< sum << " in thread " << *((int *)args) <<endl;
//释放锁,供其他线程使用
pthread_mutex_unlock(&sum_mutex);
//线程退出时添加退出的信息,status供主程序提取线程的结束信息
pthread_exit(0);
return NULL;
}
};
//线程创建时属性参数的设置 pthread_attr_t 及 join功能的使用
/*
线程的属性由结构体pthread_attr_t进行管理。
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set; void * stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;
*/
int _tmain(int argc, _TCHAR* argv[])
{
pthread_t tids[NUM_THREADS]; //进程 id
int indexex[NUM_THREADS]; //用来保存i的值避免被修改
//线程属性结构体,创建线程时加入的参数
pthread_attr_t attr;
pthread_attr_init(&attr); //初始化
//设置想要指定线程属性参数,这个参数表明这个线程是可以join连接的
//join 功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//对锁进行初始化
pthread_mutex_init(&sum_mutex, NULL);
for (int i = 0; i < NUM_THREADS; i ++)
{
//参数1创建的线程id,参数2 线程参数,参数3 线程运行函数的地址,参数4函数的参数
//传入的参数必须强制转换为 void * 类型,即无类型指针,取出 i 的地址 &i
indexex[i] = i;
int ret = pthread_create(&tids[i], &attr, hhh::sayHello, (void *)&(indexex[i]));
if (ret != 0)//创建线程成功返回 0
{
std::cout<<"pthread_create error:error_code"<<endl;
}
}
//释放内存
pthread_attr_destroy(&attr);
void * status;
for (int i = 0; i < NUM_THREADS;i ++)
{
//主线程join每个线程后取得每个线程的退出信息status
int ret = pthread_join(tids[i], &status);
if (ret != 0)
{
std::cout<< "pthread_join error = "<<ret <<endl;
}
}
std::cout<<"finally sum is "<<sum <<endl;
//注销锁
pthread_mutex_destroy(&sum_mutex);
//等待各个线程退出后,进程才结束,否则进程强制结束,线程处于未终止的状态
pthread_exit(NULL);
return 0;
}
运行如下
再次运行
再次运行
//每次运行效果不一样
//可见sum的访问和修改顺序是正常的,这就达到了多线程的目的了,但是线程的运行顺序还是混乱的