C++多线程学习---线程间的共享数据

多线程间的共享数据如果不加以约束是有问题的。最简单的方法就是对数据结构采用某种保护机制,通俗的表达就是:

确保只有进行修改的线程才能看到不变量被破坏时的中间状态。从其他访问线程的角度来看,修改不是已经完成了,就是还没开始。

1.使用互斥量保护共享数据

    当访问共享数据前,使用互斥量将相关数据锁住,再当访问结束后,再将数据解锁。C++标准库为互斥量提供了一个RAII语法的模板类std::lack_guard ,其会在构造的时候提供已锁的互斥量,并在析构的时候进行解锁.

#include <list>
#include <mutex>
#include <algorithm>
std::list<int> some_list; // 1
std::mutex some_mutex; // 2
void add_to_list(int new_value)
{
    std::lock_guard<std::mutex> guard(some_mutex); // 3
    some_list.push_back(new_value);
}
bool list_contains(int value_to_find)
{
    std::lock_guard<std::mutex> guard(some_mutex); // 4
    return std::find(some_list.begin(),some_list.end(),value_to_find) != some_list.end();
}
上述例子通过lock_guart<std::mutex>使得add_to_list和list_contains这两个函数对数据的访问是互斥的。从而保证多线程中数据的安全。


2.堤防接口内在的条件竞争

比如你在写一个栈的数据结构,并且给栈的push\pop\top\empty\size\等接口增加了互斥保护。看以下代码:

stack<int> s;
if (! s.empty()){ // 1
int const value = s.top(); // 2
s.pop(); // 3
do_something(value);
}
以上只是单线程安全,对于多线程在1和2之间,可能有来自另一个线程的pop()调用并删除了最后一个元素,此时就会出问题。

因为锁的粒度太小,需要保护的操作并未全覆盖到。可以考虑适当增大锁的粒度。

std::shared_ptr<T> pop()
{
    std::lock_guard<std::mutex> lock(m);
    if(data.empty()) throw empty_stack(); // 在调用pop前,检查栈是否为空
    std::shared_ptr<T> const res(std::make_shared<T>(data.top())); // 在修改堆栈前,分配出返回值
    data.pop();
return res;
}
void pop(T& value)
{
    std::lock_guard<std::mutex> lock(m);
    if(data.empty()) throw empty_stack();
    value=data.top();
    data.pop();
}

3.堤防死锁问题

造成死锁最大的问题是:由两个或两个以上的互斥量来锁定一个操作。一对线程需要对他们所有的互斥量做一些操作,其中每个线程都有一个互斥量,且等待另一个解锁。这样没有线程能工作,因为他们都在等待对方释放互斥量。

避免死锁的一般建议,就是让两个互斥量总以相同的顺序上锁:总在互斥量B之前锁住互斥量A,可以有效防止大部分问题。一个更好的方法是利用C++提供的std::lock,可以一次性锁住多个互斥量。看以下例子:

class some_big_object;
void swap(some_big_object& lhs,some_big_object& rhs);
class X
{
private:
    some_big_object some_detail;
    std::mutex m;
public:
    X(some_big_object const& sd):some_detail(sd){}
    friend void swap(X& lhs, X& rhs)
    {
        if(&lhs==&rhs)
    	  return;
        std::lock(lhs.m,rhs.m); // 1
        std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock); // 2
        std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock); // 3
        swap(lhs.some_detail,rhs.some_detail);
     }
};
调用std::lock() ①锁住两个互斥量,并且两个std:lock_guard 实例已经创建好②③,还有一个互斥量。提供std::adopt_lock 参数除了表示std::lock_guard 对象已经上锁外,还表示现成的锁,而非尝试创建新的锁。

注意:一个互斥量可以在同一线程上多次上锁(std::recursive_mutex)。

以下是设计锁的几条忠告:

1.尽量避免嵌套锁,一个线程已获得锁时别再获取第二个

2.避免在持有锁是调用用户提供的代码,因为用户的代码不可预知,有发生死锁的可能

3.使用固定顺序获取锁--多于多个锁时可有效避免死锁

对于锁的粒度问题也是需要注意的,大的粒度可以保护更多的数据,但是其对性能的影响也越大。同时需要尽可能他将锁的持有时间减到最小。

比较操作符一次锁住一个互斥量:

friend bool operator==(Y const& lhs, Y const& rhs)
{
    if(&lhs==&rhs)
        return true;
    int const lhs_value=lhs.get_detail(); // 2
    int const rhs_value=rhs.get_detail(); // 3
    return lhs_value==rhs_value; // 4
}




利用int的拷贝来减少锁的等待时间,是一种高效的做法。


  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中,处理多线程的常见方法是使用线程库,如pthread库。对于多线程处理for循环,可以将for循环的迭代任务分配给不同的线程并行执行,以提高程序的执行效率。 下面是一个简单的示例代码,演示了如何在C语言中使用多线程处理for循环: ```c #include <stdio.h> #include <pthread.h> #define NUM_THREADS 4 // 定义线程数量 #define ARRAY_SIZE 100 // 定义数组大小 int array = i + 1; } pthread_t threads[NUM_THREADS]; int thread_ids[NUM_THREADS]; // 创建多个线程,并分配不同的线程ID for (int i = 0; i < NUM_THREADS; i++) { thread_ids[i = i; pthread_create(&threads[i], NULL, thread_func, &thread_ids[i]); } // 等待所有线程执行完毕 for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("Sum: %d\n", sum); // 输出累加结果 return 0; } ``` 在上述代码中,我们使用了pthread库来创建多个线程,并将for循环的任务分配给不同的线程进行并行处理。每个线程通过计算起始位置和结束位置来确定自己需要处理的数组部分。线程通过累加的方式对数组进行处理,并将结果保存在全局变量sum中。最后,主线程等待所有子线程执行完毕,并输出累加结果。 请注意,此示例仅为了演示多线程处理for循环的基本概念,实际应用中可能需要更复杂的任务分配和同步机制。具体的实现方式和使用的线程库可能会有所差异,可以根据具体需求选择合适的方法和库。<span class="em">1</span> #### 引用[.reference_title] - *1* [毕设项目:C++ Qt 基于数据库Mysql学生信息管理系统.zip](https://download.csdn.net/download/qq_35831906/88258935)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值