简体   繁体   English

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

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

Problem: Let's say we have n threads where each thread receives a random unique number between 1 and n.问题:假设我们有 n 个线程,每个线程接收一个介于 1 和 n 之间的随机唯一数。 And we want the threads to print the numbers in sorted order.我们希望线程按排序顺序打印数字。

Trivial Solution (using n semaphore/mutex): We can use n mutex locks (or similarly semaphores) where thread i waits to acquire mutex lock number i and unlocks number i + 1. Also, thread 1 has no wait.简单的解决方案(使用 n 个信号量/互斥量):我们可以使用 n 个互斥锁(或类似的信号量),其中线程 i 等待获取互斥锁编号 i 并解锁编号 i + 1。此外,线程 1 没有等待。

However, I'm wondering if it's possible to simulate a similar logic using a single semaphore (of type sem_t) to implement the following logic: (i is a number between 1 to n inclusive)但是,我想知道是否可以使用单个信号量(sem_t 类型)来模拟类似的逻辑来实现以下逻辑:(i 是介于 1 到 n 之间的数字)

Thread with number i as input, waits to acquire a count of (i-1) on the semaphore, and after printing, releases a count of i.以编号 i 作为输入的线程,等待获取信号量上的计数 (i-1),并在打印后释放计数 i。 Needless to say, thread one does not wait.不用说,线程一不等待。

I know that unlike Java, sem_t does not support arbitrary increase/decrease in the semaphore value.我知道与 Java 不同,sem_t 不支持任意增加/减少信号量值。 Moreover, writing a for loop to do (i-1) wait and i release won't work because of asynchrony.此外,由于异步,编写一个 for 循环来执行 (i-1) 等待和 i 释放将无法正常工作。

I've been looking for the answer for so long but couldn't find any.我一直在寻找答案很长时间,但找不到任何答案。 Is this possible in plain C?这在普通的 C 中是否可行? If not, is it possible in C++ using only one variable or semaphore?如果不是,是否可以在 C++ 中仅使用一个变量或信号量? Overall, what is the least wasteful way to do this with ONE semaphore.总体而言,使用 ONE 信号量做到这一点最不浪费的方法是什么。

Please feel free to edit the question since I'm new to multi-threaded programming.由于我是多线程编程的新手,请随时编辑问题。

Thats a good question although, I fear you might have a XY problem since I can not imagine a good reason for your problem scenario.这是一个很好的问题,但我担心你可能有 XY 问题,因为我无法想象你的问题场景的充分理由。 Never the less, after 1-2 minutes I came up with 2 solutions with pros and cons, but I think one is perfect for you:尽管如此,在 1-2 分钟后,我想出了 2 个各有利弊的解决方案,但我认为一个对你来说是完美的:

A. When your threads are almost done the same time and or need their print ASAP you could use a shared std::atomic<T> with T=unsigned,int,size_t,uint32_t what ever you like, or any of the integer atomics in the C standard library when using C, initialise it with 0, and now every thread i busy waits until its value is i-1. A. 当您的线程几乎同时完成并且/或者需要尽快打印时,您可以使用共享的std::atomic<T>T=unsigned,int,size_t,uint32_t任何您喜欢的,或任何 integer 原子在 C 标准库中使用 C 时,将其初始化为 0,现在我忙的每个线程都在等待,直到其值为 i-1。 If so, it prints and then adds 1 on the atomic.如果是这样,它会打印然后在原子上添加 1。 Of course since of the busy wait, you will have much CPU load when thread are waiting long, and slow down, when many are waiting.当然,由于繁忙的等待,当线程等待很长时间时,您将有很多 CPU 负载,而当许多线程等待时,您会减慢速度。 But you get your print ASAP但是你尽快得到你的印刷品

B. You just store your result of thread i in a container, maybe along with its index, since I guess you want more to just print i, and after all threads are finished or periodically, sort this container and then print it. B. 您只需将线程 i 的结果存储在一个容器中,可能连同它的索引一起,因为我猜您想要更多只打印 i,并且在所有线程完成或定期完成后,对该容器进行排序然后打印它。

A.:一个。:

#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";
}

Works also in C, just use the atomics there.也适用于 C,只需使用那里的原子。

You can do this with a condition_variable in C++, which is equivalent to a pthread_cond_t with the pthreads library in C.您可以使用 C++ 中的 condition_variable 来执行此操作,这相当于使用 C 中的 pthreads 库的pthread_cond_t

What you want to share between threads is a pointer to a condition_variable, number, and a mutex to guard access to the number.您要在线程之间共享的是指向条件变量、数字和互斥锁的指针,以保护对数字的访问。

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

Each thread simply invokes a function that waits for its number to be set:每个线程简单地调用一个 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
}

And then a program to test it all out.然后是一个程序来测试它。 This one uses 10 threads.这个使用 10 个线程。 You can modify it to use more and then have your own randomization algorithm of the numbers list.您可以修改它以使用更多,然后拥有自己的数字列表随机化算法。

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;
}

All of the above is in C++.以上所有内容都在 C++ 中。 But it would be easy to transpose the above solution to C using pthreads .但是使用pthreads将上述解决方案转换为 C 很容易。 But I'll leave that as an exercise for the OP.但我会把它作为 OP 的练习。

I'm not sure if this satisfies your "one semaphore requirement".我不确定这是否满足您的“一个信号量要求”。 The mutex technically has a semaphore.互斥体在技术上具有信号量。 Not sure if the condition_variable itself has a semaphore for its implementation.不确定 condition_variable 本身是否具有用于其实现的信号量。

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

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