线程处理 returns 意外结果 - C++

Threading returns unexpected result - c++

我正在学习有关线程的作业,并且我尝试在我编写的一个简单程序上实现线程。没有线程程序可以完美运行,但是当我线程化两个随机数生成器函数时,它 returns 不正确的结果。两个数字生成器的结果似乎总是“42”,不确定为什么会这样。

同样对于上下文,我只是从线程开始,所以我知道这个程序不需要多线程。我这样做只是为了学习目的。

感谢您的帮助!

// struct for vector to use
struct readings {
    std::string name;
    int data;
};

// random generator for heat value - stores in vector of struct
void gen_heat(std::vector<readings>& storage) {
    readings h = {"Heat", rand() % 100 + 1};
    storage.insert(storage.begin(), h);
}

// random generator for light value - stores in vector of struct
void gen_light(std::vector<readings>& storage) {
    readings l = {"Light", rand() % 100 + 1};
    storage.insert(storage.begin(), l);
}

int main() {
    // vector of readings struct
    std::vector<readings> storage;

    srand(time(NULL));

    // initialising threads of random generators
    std::thread H(gen_heat, std::ref(storage));
    std::thread L(gen_light, std::ref(storage));

    // waiting for both to finish
    H.join();
    L.join();

    // print values in vec of struct
    for (const auto& e : storage) {
        std::cout << "Type: " << e.name << std::endl
                  << "Numbers:  " << e.data << std::endl;
    }

    // send to another function
    smartsensor(storage);

    return 0;
}

由于您有多个线程访问一个共同资源,在本例中为读数向量,并且其中一些线程正在修改它,因此您需要独占访问该资源。同步访问的方式有很多种;其中之一是 binary semaphore(C++20 起),它足够简单且不会深入到互斥体的使用。你基本上:

  1. 通过获取信号量拥有对资源的访问权,
  2. 使用资源,然后,
  3. 释放信号量以便其他人可以访问该资源。

如果线程 A 试图获取信号量,而其他线程 B 正在使用资源,线程 A 将阻塞,直到资源被释放。

请注意,信号量已初始化为 1,表示资源空闲。一旦一个线程获取了信号量,计数将下降到 0,并且在计数回到 1 之前没有其他线程能够获取它(在 [=18= 之后会发生什么) ]).

[Demo]

#include <cstdlib>  // rand
#include <iostream>  // cout
#include <semaphore>
#include <string>
#include <thread>
#include <vector>

std::binary_semaphore readings_sem{1};

// struct for vector to use
struct readings {
    std::string name;
    int data;
};

// random generator for heat value - stores in vector of struct
void gen_heat(std::vector<readings>& storage) {
    for (auto i{0}; i < 5; ++i) {
        readings_sem.acquire();
        readings h = {"Heat", rand() % 100 + 1};
        storage.insert(storage.begin(), h);
        readings_sem.release();
    }
}

// random generator for light value - stores in vector of struct
void gen_light(std::vector<readings>& storage) {
    for (auto i{0}; i < 5; ++i) {
        readings_sem.acquire();
        readings l = {"Light", rand() % 100 + 1};
        storage.insert(storage.begin(), l);
        readings_sem.release();
    }
}

int main() {
    // vector of readings struct
    std::vector<readings> storage;

    srand(time(NULL));

    // initialising threads of random generators
    std::thread H(gen_heat, std::ref(storage));
    std::thread L(gen_light, std::ref(storage));

    // waiting for both to finish
    H.join();
    L.join();

    // print values in vec of struct
    for (const auto& e : storage) {
        std::cout << "Type: " << e.name << std::endl
            << "Numbers:  " << e.data << std::endl;
    }
}

// Outputs (something like):
//
//   Type: Heat
//   Numbers:  5
//   Type: Light
//   Numbers:  83
//   Type: Light
//   Numbers:  40
//   ...

[Ben Voigt 评论的更新]

资源的获取和释放可以使用RAII(Resource Acquisition Is Initialization)封装,语言已经提供了这种机制。例如:

  • 两个线程仍然尝试获取互斥锁以访问读数资源向量。
  • 但他们通过创建 lock guard.
  • 获得它
  • 一旦锁守卫超出范围并被销毁,互斥体就会被释放。

[Demo]

#include <mutex>  // lock_guard

std::mutex mtx{};

// random generator for heat value - stores in vector of struct
void gen_heat(std::vector<readings>& storage) {
    for (auto i{0}; i < 5; ++i) {
        std::lock_guard<std::mutex> lg{ mtx };
        readings h = {"Heat", rand() % 100 + 1};
        storage.insert(storage.begin(), h);
    }
}