使用原子和互斥锁 c++ 在类内部进行线程化

Posted

技术标签:

【中文标题】使用原子和互斥锁 c++ 在类内部进行线程化【英文标题】:Threading inside class with atomics and mutex c++ 【发布时间】:2016-12-05 05:19:05 【问题描述】:

我编写了这个示例程序来模仿我在一个更大的程序中尝试做的事情。

我有一些数据将来自用户并被传递到线程中进行一些处理。我在数据周围使用互斥锁,当有数据时,标志会发出信号。

使用 lambda 表达式,是否将指向 *this 的指针发送到线程?我似乎在 cout 声明中得到了我期望的行为。

是否在数据周围正确使用了互斥锁?

将原子和互斥锁作为类的私有成员是一个好的举措吗?

foo.h

#pragma once
#include <atomic>
#include <thread>
#include <vector>
#include <mutex>

class Foo

public:
    Foo();
    ~Foo();

    void StartThread();
    void StopThread();
    void SendData();

private:
    std::atomic<bool> dataFlag;
    std::atomic<bool> runBar;
    void bar();
    std::thread t1;
    std::vector<int> data;
    std::mutex mx;
;

foo.c

#include "FooClass.h"
#include <thread>
#include <string>
#include <iostream>

Foo::Foo()

    dataFlag = false;


Foo::~Foo()

    StopThread();


void Foo::StartThread()

    runBar = true;
    t1 = std::thread([=] bar(); );
    return;


void Foo::StopThread()

    runBar = false;

    if(t1.joinable())
        t1.join();

    return;


void Foo::SendData()

    mx.lock();
    for (int i = 0; i < 5; ++i) 
        data.push_back(i);
    
    mx.unlock();
    dataFlag = true;


void Foo::bar()

    while (runBar)
    
        if(dataFlag)
        
            mx.lock();
            for(auto it = data.begin(); it < data.end(); ++it)
            
                std::cout << *it << '\n';
            
            mx.unlock();
            dataFlag = false;
        
    

main.cpp

#include "FooClass.h"
#include <iostream>
#include <string>

int main()

    Foo foo1;

    std::cout << "Type anything to end thread" << std::endl;

    foo1.StartThread();
    foo1.SendData();

    // type something to end threads
    char a;
        std::cin >> a;

    foo1.StopThread();

    return 0;

【问题讨论】:

使用 std::lock_guard 而不是手动调用 mx.lock()/unlock() 当你启动线程时,你应该join()它或者detach() @DmitryKatkevich 为什么?他稍后会join()ing... 我更喜欢保存 detach 以备不时之需 我会将您的作业转移到 dataFlag 到锁内。现在我可以看到它在外面的潜在问题。 【参考方案1】:

您确保线程是使用 RAII 技术连接的?查看。

所有数据访问/修改都通过atomics 或mutexs 保护?查看。

互斥锁使用std::lock_guard?没有。使用std::lock_guard 将您的lock()unlock() 调用与RAII 一起包装。这确保即使在锁内发生异常,锁也会被释放。

将原子和互斥锁作为类的私有成员是一个好的举措吗?

它既不好也不坏,但在这种情况下,Foostd::thread 的包装器,它可以工作并控制同步,这是有道理的。

使用 lambda 表达式,是否将指向 *this 的指针发送到线程?

是的,您也可以使用t1 = std::thread([this]bar();); 使其更明确。

就目前而言,在锁定之后分配dataFlag,您可能会遇到问题。如果您调用SendData 两次以使bar 处理第一个,但在设置dataFlag = false 之前暂停,以便第二个调用添加数据,则将标志设置为true 仅让bar 将其设置回false。然后,您将拥有已“发送”但bar 认为没有任何要处理的数据。

可能还有其他棘手的情况,但这只是一个例子;将其移入锁中可以解决该问题。

例如,您的 SendData 应如下所示:

void Foo::SendData()

    std::lock_guard<std::mutex> guard(mx);
    for (int i = 0; i < 5; ++i) 
        data.push_back(i);
    
    dataFlag = true;

【讨论】:

以上是关于使用原子和互斥锁 c++ 在类内部进行线程化的主要内容,如果未能解决你的问题,请参考以下文章

C++多线程1.2-线程安全的保证——互斥量mutex(锁)和原子变量atomic

C++线程间的互斥和通信

C++线程间的互斥和通信

互斥锁,自旋锁,原子操作原理和实现

多线程编程之原子锁

线程互斥锁