使用互斥锁时尝试引用已删除的函数

10

我在做一个项目时遇到了奇怪的错误。我创建了一个超级简单的示例来重现这个错误。

我创建了一个类。在这个类中,我希望有一种“getter”函数可以填充一个struct的值。在主应用程序中,用户会实例化这个struct,将其传递给一个成员函数,并能够在返回时读取struct中的值。由于实际类的设计,这必须在一个单独的线程中进行。这是我的代码:

myClass.h:

#ifndef __MY_CLASS_H__
#define __MY_CLASS_H__

#include <mutex>

class myClass {
public:
    struct my_struct_s {
        int field1;
        short field2;
    };

    int get_data(my_struct_s & my_struct);

private:

};

#endif /* __MY_CLASS_H__ */

myClass.cpp:

#include "myClass.h"

int myClass::get_data(struct my_struct_s & my_struct)
{
    int var1 = 5;
    char var2 = 2;

    my_struct.field1 = var1;
    my_struct.field2 = var2;

    return 0;
}

Main.cpp:

#include "myClass.h"
#include <iostream>
#include <thread>
#include <Windows.h>

bool thread_running;
std::thread thread;

void run_thread(myClass & lmyClass)
{
    myClass::my_struct_s my_struct;

    while (thread_running) {
        lmyClass.get_data(my_struct);

        std::cout << my_struct.field1 << std::endl;
        std::cout << my_struct.field2 << std::endl;

        Sleep(100);
    }
}

int main(int argc, char *argv[])
{
    myClass lmyClass; 

    thread_running = true;
    thread = std::thread(run_thread, lmyClass);

    Sleep(1000);
    thread_running = false;

    if (thread.joinable()) {
        thread.join();
    }

    getchar();

    return 0;
}

它的表现如预期。但是,由于该类的异步性质,我需要使用互斥锁来保护在类内不同线程中处理的数据。

如果我将std::mutex作为我的类的私有成员添加进去,在尝试运行代码时会收到以下错误:

Error 1 error C2280: 'std::mutex::mutex(const std::mutex &)' : attempting to reference a deleted function ...

1) 我正在尝试理解为什么会出现此错误。

2) (这部分有点基于意见)考虑到以上信息,通过一种'getter'的方式填充公共结构体以便实现我的类可以操纵其中的变量,是否是一个好的设计?还有更好的方法吗?


1
请展示在尝试使用std::mutex时出现问题的实际代码。 - Remy Lebeau
1
正如已经提到的,std::mutex是不可复制的。它也不可移动。但是如果您想要,您可以拥有unique_ptr来管理mutex。顺便说一下,我会将thread_running改为atomic<bool>。 - Severin Pappadeux
1
不确定我是否理解:如果它是全局的,那么它更像是单例,但如果您在一个线程中访问它,那么为什么要费心使用互斥锁?您能发布/发送代码吗?顺便说一下,如果没有什么可做的,您可能需要将Sleep(100)替换为std :: this_thread :: yield(),主动放弃时间片。 - Severin Pappadeux
该类本身有一个私有线程,它始终从串口读取数据。它具有像 start()stop()set_mode() 这样的公共成员,这些成员会通过串口发送消息,但是读取线程永远不会停止。它只是在知道响应帧后处理响应帧。一旦帧被识别,它们就被放入队列中,这就是我想让我的“getter”函数从中提取数据的地方。这就需要互斥锁,显然(异步队列访问)。 - justynnuff
1
在这种情况下(读取线程和处理线程),您可能需要使用条件变量。作为一个简单的例子(尽管我会改用atomic<bool>),请参考http://en.cppreference.com/w/cpp/thread/condition_variable或者这里的讨论:https://dev59.com/0WYr5IYBdhLWcg3w0tWE。 - Severin Pappadeux
显示剩余2条评论
2个回答

17

myclass中你没有定义拷贝构造函数,因此编译器会提供一个默认的拷贝构造函数。这会尝试拷贝所有成员,包括你的不可拷贝对象std::mutex。正如编译器错误所述,它被标记为已删除。

你需要定义自己的拷贝构造函数;很可能你想在拷贝实例时获取存储在其中的互斥锁,然后将其所有其他成员复制到新实例中。


出于好奇,这是否意味着所有需要 std:mutex 作为成员的类都需要用户定义的复制构造函数? - Kam
1
@Kam:只有在尝试调用类的复制构造函数时才会这样。 - Collin Dauphinee
嗯,如果您希望每个副本都拥有自己的互斥实例,那么有可能吗? - Kam
1
@Kam:如果你的类有一个“std::mutex”成员,每个实例将拥有自己独特的互斥锁。 - Collin Dauphinee

2

请动态声明互斥锁,不要静态声明。

在您的myClass类头中将互斥锁声明为指针,例如:

mutex * mtx;

在你的构造函数中调用互斥锁的构造函数来初始化互斥锁:

mtx = new std::mutex();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接