资源竞争
当一个进程中多个线程同时对一个共享资源进行访问时将可能导致数据不一致问题;
#define NUM 5
int g_val = 700;
class threadData {
// 封装一个线程的基本信息 用于描述线程
public:
threadData(const int number) {
threadname_ = "Thread_" + to_string(number); }
public:
string threadname_;
pthread_t tid_;
};
void *threadRoutine(void *args) {
threadData *td =
static_cast<threadData *>(args); // 接收线程的管理结构体并进行类型强转
td->tid_ = pthread_self(); // 存储自己的 tid 方便后续直接访问
while (true) {
if (g_val > 0) {
usleep(100);
printf("I am %s , the g_val = %d\n", td->threadname_.c_str(), g_val);
g_val--;
} else
break;
}
delete td;
return nullptr;
}
int main() {
vector<pthread_t> tids; // 用于管理线程的tid
for (size_t i = 0; i < NUM; ++i) {
pthread_t tid;
threadData *td = new threadData(i); // 用 new 在堆上实例化一个对象
// 防止 for 循环后变量丢失
pthread_create(&tid, nullptr, threadRoutine, td); // 创建线程
tids.push_back(tid); // 将线程tid存放至STL容器中
// 方便主线程后期对这些线程进行管理
}
for (size_t i = 0; i < tids.size(); ++i) {
// 循环调用 pthread_join 等待线程
pthread_join(tids[i], nullptr);
}
return 0;
}
以该段代码为例;
该程序中创建了5
个线程同时对全局数据g_val
进行访问;
条件为当g_val > 0
时进行--
操作,否则break
跳出循环;
最终运行结果为:
$ ./mythread
I am Thread_1 , the g_val = 700
I am Thread_2 , the g_val = 700
I am Thread_0 , the g_val = 700
I am Thread_4 , the g_val = 700
I am Thread_3 , the g_val = 699
I am Thread_1 , the g_val = 695
...
...
I am Thread_0 , the g_val = 2
I am Thread_4 , the g_val = 1
I am Thread_3 , the g_val = 0
I am Thread_1 , the g_val = -1
I am Thread_2 , the g_val = -1
I am Thread_0 , the g_val = -3
即使设置了条件判断,最终的结果也导致了g_val
的值变为了-3
;
其中g_val--
操作并不是原子性的;
该问题即为多线程进行资源竞争导致的数据不一致问题,可参考CSDN -『 Linux 』线程的资源共享,分离,以及多线程并发导致资源竞争;
可用 锁 来保证多线程对一个共享资源的串型访问来放置该问题的发生;
pthread_mutex 互斥锁
pthread
库提供了一系列的锁的接口以使得用户能通过使用锁来对多线程访问统一共享资源时提供保护;
pthread
库中的锁为pthread_mutex
锁,是一种互斥锁;
PROLOG
This manual page is part of the POSIX Programmer's Manual. The Linux implementation of this interface may differ (consult the corresponding Linux
manual page for details of Linux behavior), or the interface may not be implemented on Linux.
NAME
pthread_mutex_destroy, pthread_mutex_init - destroy and initialize a mutex
SYNOPSIS
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
RETURN VALUE
If successful, the pthread_mutex_destroy() and pthread_mutex_init() functions shall return zero; otherwise, an error number shall be returned to
indicate the error.
The [EBUSY] and [EINVAL] error checks, if implemented, act as if they were performed immediately at the beginning of processing for the function
and shall cause an error return prior to modifying the state of the mutex specified by mutex.
该系列接口为 POSIX 线程库(pthread
)中的核心部分;
用于实现互斥锁的创建,初始化,销毁,静态初始化等操作;
-
pthread_mutex_t
这是一个互斥锁类型的定义,通常在
pthread.h
头文件中定义;互斥锁对象在初始化后可以用于控制对共享资源的访问;
在使用锁的前提是必须存在一把锁,故在使用互斥锁之前必须使用该类型定义一把锁,如:
int main(){ pthread_mutex_t lock; // 定义一把名为 lock 的锁 return 0; }
-
pthread_mutex_init()
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
该接口的功能是初始化一个互斥锁对象,使其可以用于锁定和解锁操作,其参数如下:
-
pthread_mutex_t *restrict mutex
该参数为指向要初始化的互斥锁对象指针;
-
const pthread_mutexattr_t *restrict attr
该参数指向互斥锁属性对象的指针;
当该参数传入
nullptr
时表示使用默认属性;
函数调用成功时返回
0
,失败时返回错误码,一般可能出现的错误码为:-
EBUSY
表示该互斥锁正在被使用中(通常这个错误不会在初始化时出现);
-
EINVAL
表示传递的参数无效,通常是因为传递的属性对象不是一个有效的属性对象;
-
-
pthread_mutex_destroy()
int pthread_mutex_destroy(pthread_mutex_t *mutex);
该接口用于销毁一个互斥锁对象,使其不可再被使用,参数为如下:
-
pthread_mutex_t *mutex
该参数表示传入一个需要被销毁的互斥锁对象指针;
函数调用时返回
0
,失败时返回错误码,其可能出现的错误码与pthread_mutex_init()
相同; -
-
PTHREAD_MUTEX_INITIALIZER
宏该宏提供了一种简单的静态初始化互斥锁的方法;
当在全局区域中使用该宏定义了一个互斥锁时,该互斥锁将可不使用
pthread_mutex_inti()
进行初始化以及使用pthread_mutex_destroy()
进行销毁;当锁被使用完后将被系统自动回收;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
上述接口只是单纯的对互斥锁进行定义,初始化,以及销毁操作,而锁的使用还涉及到一系列接口:
PROLOG
This manual page is part of the POSIX Programmer's Manual. The Linux implementation of this interface may differ (consul