『 Linux 』多线程互斥锁


资源竞争

请添加图片描述

当一个进程中多个线程同时对一个共享资源进行访问时将可能导致数据不一致问题;

#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
在嵌入式Linux中使用多线程互斥锁可以避免多个线程同时访问共享资源而产生的竞态条件问题。下面是使用互斥锁的步骤: 1. 定义互斥锁变量 ```c pthread_mutex_t mutex; ``` 2. 初始化互斥锁 ```c pthread_mutex_init(&mutex, NULL); ``` 3. 在需要访问共享资源的代码段前加锁 ```c pthread_mutex_lock(&mutex); // 访问共享资源的代码段 pthread_mutex_unlock(&mutex); ``` 4. 在访问共享资源的代码段结束后解锁 ```c pthread_mutex_unlock(&mutex); ``` 需要注意的是,对于同一个互斥锁变量,加锁和解锁的线程必须是同一个线程,否则会导致死锁。此外,如果线程在加锁时发现锁已经被其他线程占用,则会被阻塞,直到锁被释放。 下面是一个简单的示例代码: ```c #include <pthread.h> #include <stdio.h> pthread_mutex_t mutex; int counter = 0; void* thread_function(void* arg) { int i; for (i = 0; i < 100000; i++) { pthread_mutex_lock(&mutex); counter++; pthread_mutex_unlock(&mutex); } return NULL; } int main() { pthread_t thread1, thread2; pthread_mutex_init(&mutex, NULL); pthread_create(&thread1, NULL, thread_function, NULL); pthread_create(&thread2, NULL, thread_function, NULL); pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf("counter = %d\n", counter); pthread_mutex_destroy(&mutex); return 0; } ``` 在这个示例中,两个线程分别对counter变量进行100000次加1操作,使用互斥锁保证了计数器的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dio夹心小面包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值