使用互斥锁将一次运行的线程数限制为 2

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

本文关键字:线程 运行 一次      更新时间:2023-10-16

我有一个程序,它将 10 个线程推送到一个向量中,每个线程应该在完成之前打印出一个字符 5 次("A"表示第一个线程,"B"表示第二个线程,依此类推)。我能够让它们一次运行所有内容(使用 detach())或让它们一次运行一个(使用 join())。现在我想使用互斥体将一次允许打印的线程数限制为 2。我已经能够声明互斥锁并将锁放置到位,但我不确定如何应用这样的限制。有人对如何进行有任何想法吗?

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会立即从 deque 中弹出该线程。 最好在调用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();