使用 Mutex 将线程数 运行 一次限制为 2

Using a Mutex to Limit the Number of Threads Running at a Time to 2

我有一个程序将 10 个线程压入一个向量,每个线程应该在完成之前打印出一个字符 5 次(第一个线程 'A',第二个线程 'B' , ETC)。我可以让它们一次全部达到 运行(使用 detach()),或者一次达到一个 运行(使用 join())。现在我想使用 Mutex 将一次允许打印的线程数限制为 2。我已经能够声明 mutex 并将锁放在适当的位置,但我不确定如何应用这样的限制.有人对如何进行有任何想法吗?

deque<int> q ;
mutex print_mutex ;
mutex queue_mutex ;
condition_variable queue_cond ;

void begin(int num) {
    unique_lock<mutex> ul {queue_mutex};
    q.emplace_back(num);
    queue_cond.wait(ul,[num]{
        return q.front() == num; });
    q.pop_front();
    cout << num << " leaves begin " << endl ;
}

void end ( int num ) {
    lock_guard<mutex>lg{queue_mutex};
    queue_cond.notify_all();
    cout << num << " has ended " << endl ;
}

void run(int num, char ch) {
    begin(num);
    for (int i = 0; i < 5; ++i) {
        {
            lock_guard<mutex> lg { print_mutex };
            cout << ch << endl << flush ;
        }
        sleep_for(milliseconds(250));
    }
    end(num);
}

int main() {
    vector<thread>threads {};
    for (int i = 0; i < 10; ++i) {
        threads.push_back(thread{run,i,static_cast<char>(65+i)});
        threads.at(i).join();
    }
}

您已经使用全局 deque<int> q 为您的线程设置了一个 FIFO。所以让我们使用它。

目前,您正在尝试限制执行,直到当前线程位于最前面。虽然有一个错误,因为 begin 会立即从双端队列中弹出该线程。最好在调用 end 时删除该值。首先是更改:

void end(int num)
{
    {
        lock_guard<mutex>lg{queue_mutex};
        cout << num << " has ended " << endl ;
        q.erase(find(q.begin(), q.end(), num));
    }
    queue_cond.notify_all();
}

这使用 <algorithm> 中的 std::find 来删除特定值。您可以使用 pop_front,但我们将更改该逻辑以使其更通用。另请注意,通知时不需要锁定条件变量。

因此,将 begin 中的逻辑扩展到前两个位置并不难。这里:

void begin(int num)
{
    unique_lock<mutex> ul {queue_mutex};
    q.emplace_back(num);
    queue_cond.wait(ul,[num]{
        auto end = q.begin() + std::min(2, static_cast<int>(q.size()));
        return find(q.begin(), end, num) != end;
        });
    cout << num << " leaves begin " << endl ;
}

您可以将 2 更改为您想要的任何内容,最多允许那么多线程通过。在某些时候,您可能会放弃这种方法并使用更简单的方法,例如单个计数器变量,然后依靠线程调度程序来管理唤醒哪个线程,而不是将它们强制放入您的 FIFO 中。这样你就可以切换到使用 notify_one 来唤醒单个线程并减少切换开销。

无论如何,最后要做的是从线程生成循环中删除 join。并发现在由 beginend 管理。所以你会这样做:

for (int i = 0; i < 10; ++i) {
    threads.push_back( thread{run, i, 'A'+i} );
}
for (auto & t : threads) t.join();