簡體   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