C/C++ - sem_t 类型的单个信号量按顺序打印数字
Posted
技术标签:
【中文标题】C/C++ - sem_t 类型的单个信号量按顺序打印数字【英文标题】:C/C++ - Single semaphore of type sem_t to print numbers in order 【发布时间】:2019-10-02 23:16:17 【问题描述】:问题:假设我们有 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 信号量做到这一点最浪费的方法是什么。
由于我是多线程编程的新手,请随时编辑问题。
【问题讨论】:
这对于std::conditional_variable
来说是微不足道的。可以用吗?
@selbie 我更喜欢一个也可以在纯 C 上工作的答案。不过,我也想知道您的解决方案。
告诉我们更多关于 i 的信息:我猜它是一个所有大于 0 的自然数的元素?你知道它的上限吗?我从你的问题中假设它是连续的?哪个 C++ 标准? C++20 好吗?
是的,n 是自然正数。而 i 只是一个从 1 到 n 的计数器。
如果你想用“C”来回答,为什么把问题标记为 C++?
【参考方案1】:
您可以使用 C++ 中的 condition_variable 执行此操作,这相当于使用 C 中的 pthreads 库的pthread_cond_t。
您希望在线程之间共享的是指向条件变量、数字和互斥锁的指针,以保护对数字的访问。
struct GlobalData
std::condition_variable cv;
int currentValue;
std::mutex mut;
;
每个线程简单地调用一个函数,等待其编号被设置:
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 本身是否有用于实现的信号量。
【讨论】:
谢谢,是的,这就是我要找的,我想我会自己试试 pthread 的。很好地使用 unique_ptr 来解锁互斥锁;)【参考方案2】:这是一个很好的问题,但我担心您可能遇到 XY 问题,因为我无法想象您的问题场景的充分理由。尽管如此,在 1-2 分钟后,我想出了 2 个各有利弊的解决方案,但我认为一个对你来说是完美的:
A.当您的线程几乎同时完成并且/或者需要尽快打印时,您可以使用共享的std::atomic<T>
和T=unsigned,int,size_t,uint32_t
,无论您喜欢什么,或者在使用 C 时使用 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 中使用,只需使用那里的原子。
【讨论】:
谢谢。但我接受了另一个答案,因为我个人认为 while 循环有点浪费。顺便说一句,我认为它在 C 中不起作用。 atomic 是在 C++11 中引入的。 没有 C 从 C11 开始也有原子:en.cppreference.com/w/c/atomic【参考方案3】:以下代码使用 pthread_cond_t 并在 C 中工作。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define n 100
int counter = 0;
int used[n];
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void foo(void *given_number)
int number = (int)given_number;
pthread_mutex_lock(&mutex);
while(counter != number)
pthread_cond_wait(&cond, &mutex);
printf("%d\n", number);
counter++;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
int get_random_number()
while(1)
int x = rand()%n;
if(!used[x])
used[x] = 1;
return x;
int main()
pthread_t threads[n];
for(int i = 0; i < n; i++)
int num = get_random_number();
pthread_create(&threads[i], NULL, foo, (void *)num);
for(int i = 0; i < n; i++)
pthread_join(threads[i], NULL);
return 0;
【讨论】:
以上是关于C/C++ - sem_t 类型的单个信号量按顺序打印数字的主要内容,如果未能解决你的问题,请参考以下文章