繁体   English   中英

C/C++ - sem_t 类型的单个信号量按顺序打印数字

[英]C/C++ - Single semaphore of type sem_t to print numbers in order

问题:假设我们有 n 个线程,每个线程接收一个介于 1 和 n 之间的随机唯一数。 我们希望线程按排序顺序打印数字。

简单的解决方案(使用 n 个信号量/互斥量):我们可以使用 n 个互斥锁(或类似的信号量),其中线程 i 等待获取互斥锁编号 i 并解锁编号 i + 1。此外,线程 1 没有等待。

但是,我想知道是否可以使用单个信号量(sem_t 类型)来模拟类似的逻辑来实现以下逻辑:(i 是介于 1 到 n 之间的数字)

以编号 i 作为输入的线程,等待获取信号量上的计数 (i-1),并在打印后释放计数 i。 不用说,线程一不等待。

我知道与 Java 不同,sem_t 不支持任意增加/减少信号量值。 此外,由于异步,编写一个 for 循环来执行 (i-1) 等待和 i 释放将无法正常工作。

我一直在寻找答案很长时间,但找不到任何答案。 这在普通的 C 中是否可行? 如果不是,是否可以在 C++ 中仅使用一个变量或信号量? 总体而言,使用 ONE 信号量做到这一点最不浪费的方法是什么。

由于我是多线程编程的新手,请随时编辑问题。

这是一个很好的问题,但我担心你可能有 XY 问题,因为我无法想象你的问题场景的充分理由。 尽管如此,在 1-2 分钟后,我想出了 2 个各有利弊的解决方案,但我认为一个对你来说是完美的:

A. 当您的线程几乎同时完成并且/或者需要尽快打印时,您可以使用共享的std::atomic<T>T=unsigned,int,size_t,uint32_t任何您喜欢的,或任何 integer 原子在 C 标准库中使用 C 时,将其初始化为 0,现在我忙的每个线程都在等待,直到其值为 i-1。 如果是这样,它会打印然后在原子上添加 1。 当然,由于繁忙的等待,当线程等待很长时间时,您将有很多 CPU 负载,而当许多线程等待时,您会减慢速度。 但是你尽快得到你的印刷品

B. 您只需将线程 i 的结果存储在一个容器中,可能连同它的索引一起,因为我猜您想要更多只打印 i,并且在所有线程完成或定期完成后,对该容器进行排序然后打印它。

一个。:

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
#include <functional>

void thread_function(unsigned i, std::atomic<unsigned>& atomic) {
    while (atomic < i - 1) {}
    std::cout << i << " ";
    atomic += 1;
}

int main() {
    std::atomic<unsigned> atomic = 0;

    std::vector<std::thread> threads;
    for (auto i : {3,1,2}) {
        threads.push_back(std::thread(thread_function, i, std::ref(atomic)));
    }
    for (auto& t : threads) {
        t.join();
    }
    std::cout << "\n";
}

也适用于 C,只需使用那里的原子。

您可以使用 C++ 中的 condition_variable 来执行此操作,这相当于使用 C 中的 pthreads 库的pthread_cond_t

您要在线程之间共享的是指向条件变量、数字和互斥锁的指针,以保护对数字的访问。

struct GlobalData
{
    std::condition_variable cv;
    int currentValue;
    std::mutex mut;
};

每个线程简单地调用一个 function 等待其编号被设置:

void WaitForMyNumber(std::shared_ptr<GlobalData> gd, int number)
{
    std::unique_lock<std::mutex> lock(gd->mut);
    while (gd->currentValue != number)
    {
        gd->cv.wait(lock);
    }

    std::cout << number << std::endl;
    gd->currentValue++;
    gd->cv.notify_all(); // notify all other threads that it can wake up and check
}

然后是一个程序来测试它。 这个使用 10 个线程。 您可以修改它以使用更多,然后拥有自己的数字列表随机化算法。

int main()
{
    int numbers[10] = { 9, 1, 0, 7, 5, 3, 2, 8, 6, 4 };
    std::shared_ptr<GlobalData> gd = std::make_shared<GlobalData>();
    // gd->number is initialized to 0.

    std::thread threads[10];

    for (int i = 0; i < 10; i++)
    {
        int num = numbers[i];
        auto fn = [gd, num] {WaitForMyNumber(gd, num); };
        threads[i] = std::move(std::thread(fn));
    }

    // wait for all the threads to finish
    for (int i = 0; i < 10; i++)
    {
        threads[i].join();
    }

    return 0;
}

以上所有内容都在 C++ 中。 但是使用pthreads将上述解决方案转换为 C 很容易。 但我会把它作为 OP 的练习。

我不确定这是否满足您的“一个信号量要求”。 互斥体在技术上具有信号量。 不确定 condition_variable 本身是否具有用于其实现的信号量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM