std :: move отменяет действие этого указателя в std :: bind - PullRequest
0 голосов
/ 03 марта 2020

У меня есть следующий класс, который заботится о последовательном устройстве. (Пожалуйста, имейте в виду, что я опубликовал минимальный пример, который воспроизводит проблему, которую я наблюдаю. На самом деле существует множество обработчиков ошибок и т. Д. c, которые я удалил для простоты - поэтому не судите меня по этому вопросу)

class Device
{
public:
    void open(const std::string& path) { 
        fd = ::open(path.c_str(), O_RDWR | O_NOCTTY));
        readThread = std::thread(std::bind(&Device::readProc, this)); 
    }
    void close() { ::close(fd); }

private:
    void readProc() { auto i = read(fd, buffer, sizeof(buffer)); }
    std::thread readThread;
    int fd;
};

в основном, я перечислю каждое устройство, и если открытие было успешным, заболею его sh в вектор:

for (auto& p : std::filesystem::directory_iterator("/dev/"))
        if (p.path().string().substr(0, 11) == "/dev/ttyACM")
        {
            Device dev;
            if (dev.open(p.path().string()))
            {
                readers.push_back(std::move(dev));
            }
            else
            {
                std::cout << "Failed to open device " << p.path() << std::endl;
            }
        }

проблема в том, что после вызова std::move(dev) этот указатель, переданный функции std::bind, кажется недействительным. read возвращает -1 с errno, равным 9 (неверный дескриптор). Я отладил его, и после открытия значение fd равнялось 5 (меняется при каждом запуске программы), тогда как в readPro c () оно становилось -129589 (случайное значение) после std::move.

Это правильное поведение? Если так, как этого можно избежать?

Редактировать: Я изменил перечисление устройства на:

readers.emplace_back();
auto& dev = readers.back();
if (!dev.open(p.path().string()))
{
    readers.pop_back();
}

, который работал как шарм, таким образом подтверждая мой замечания относительно std :: move (), но это далеко не правильный способ сделать это.

1 Ответ

3 голосов
/ 03 марта 2020

Здесь:

readThread = std::thread(std::bind(&Device::readProc, this)); 

вы связываете this с функцией, переданной потоку. Затем, когда вы вызываете

readers.push_back(std::move(dev));

, dev перемещается, и элемент потока перемещается, но поток все еще имеет указатель на перемещенный объект.

В вашей фиксированной версии это не так, вы звоните open на Device, который уже находится в контейнере.

Это правильное поведение?

Да. После перемещения объекта dev все равно имеет this без изменений. И объект в контейнере - это другой this.

Если это так, как этого можно избежать?

Будьте осторожны с ссылками / указателями, передаваемыми потокам. В общем, вы должны позаботиться о том, чтобы объект, на который ссылаются / на который указывают, все еще жив.

...