- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试编写一个非常简单的线程池来了解它们在幕后是如何工作的。不过,我遇到了一个问题。当我使用 condition_variable 并调用 notify_all() 时,它只会唤醒池中的一个线程。
其他一切正常。我已经排队了 900 个作业,每个作业都有不错的有效负载。唤醒的一个线程消耗了所有这些工作,然后又回到 sleep 状态。在下一个循环中,这一切再次发生。
问题是只有一个线程完成工作!我怎么搞砸了这个模板?
线程池.h:
#pragma once
#include <mutex>
#include <stack>
#include <atomic>
#include <thread>
#include <condition_variable>
class ThreadPool
{
friend void __stdcall ThreadFunc();
public:
static ThreadPool & GetInstance()
{
static ThreadPool sInstance;
return (sInstance);
}
public:
void AddJob(Job * job);
void DoAllJobs();
private:
Job * GetJob();
private:
const static uint32_t ThreadCount = 8;
std::mutex JobMutex;
std::stack<Job *> Jobs;
volatile std::atomic<int> JobWorkCounter;
std::mutex SharedLock;
std::thread Threads[ThreadCount];
std::condition_variable Signal;
private:
ThreadPool();
~ThreadPool();
public:
ThreadPool(ThreadPool const &) = delete;
void operator = (ThreadPool const &) = delete;
};
线程池.cpp:
#include "ThreadPool.h"
void __stdcall ThreadFunc()
{
std::unique_lock<std::mutex> lock(ThreadPool::GetInstance().SharedLock);
while (true)
{
ThreadPool::GetInstance().Signal.wait(lock);
while (Job * job = ThreadPool::GetInstance().GetJob())
{
job->_jobFn(job->_args);
ThreadPool::GetInstance().JobWorkCounter--;
}
}
}
ThreadPool::ThreadPool()
{
JobWorkCounter = 0;
for (uint32_t i = 0; i < ThreadCount; ++i)
Threads[i] = std::thread(ThreadFunc);
}
ThreadPool::~ThreadPool()
{
}
void ThreadPool::AddJob(Job * job)
{
JobWorkCounter++;
JobMutex.lock();
{
Jobs.push(job);
}
JobMutex.unlock();
}
void ThreadPool::DoAllJobs()
{
Signal.notify_all();
while (JobWorkCounter > 0)
{
Sleep(0);
}
}
Job * ThreadPool::GetJob()
{
Job * return_value = nullptr;
JobMutex.lock();
{
if (Jobs.empty() == false)
{
return_value = Jobs.top();
Jobs.pop();
}
}
JobMutex.unlock();
return (return_value);
}
感谢您的帮助!抱歉发布了大代码。
最佳答案
除非您想设计一个新模式,否则使用条件变量的简单“猴子看猴子做”的方法总是有 3 个东西。
一个条件变量、一个互斥锁和一条消息。
std::condition_variable cv;
mutable std::mutex m;
your_message_type message;
然后有 3 种模式可以遵循。发送一条消息:
std::unique_lock l{m}; // C++17, don't need to pass type
set_message_data(message);
cv.notify_one();
发送大量消息:
std::unique_lock l{m};
set_lots_of_message_data(message);
cv.notify_all();
最后,等待和处理消息:
while(true) {
auto data = [&]()->std::optional<data_to_process>{
std::unique_lock l{m};
cv.wait( l, [&]{ return done() || there_is_a_message(message); } );
if (done()) return {};
return get_data_to_process(message);
}();
if (!data) break;
auto& data_to_process = *data;
// process the data
}
有一定的灵 active 。但是有许多规则需要遵循。
在设置消息数据和通知之间,您必须锁定互斥量。
您应该始终使用 wait
的 lambda 版本——在没有 lambda 版本的情况下执行此操作意味着您在 100 次中有 99 次做错。
消息数据应该足以确定是否应该完成一项任务,如果不是因为讨厌的线程和锁等等。
仅使用 RAII 方式来锁定/解锁互斥量。没有它的正确性几乎是不可能的。
处理内容时不要持有锁。保持锁定足够长的时间以处理数据,然后放下锁定。
您的代码违反了 2、3、4、5。我认为您没有搞砸 1。
但是,如果您在通知时锁定 cv,那么现代 cv 实现实际上非常高效。
我认为最明显的症状来自 3:您的工作线程始终持有锁,因此只有一个可以进行。其他人会导致您的代码出现其他问题。
现在,超越这种相对简单的模式是可能的。但是一旦你这样做了,你真的需要至少对 C++ 线程模型有一个基本的了解,你不能通过编写代码和“看看它是否有效”来学习。您必须坐下来仔细阅读规范,了解条件变量在标准中的作用,了解互斥量的作用,编写一些代码,坐下来找出为什么它不起作用,找其他人写类似的代码,它有问题,找出其他人如何调试它并发现错误,回到你的代码并找到同样的错误,调整它,然后重复。
这就是我使用条件变量编写原语的原因,我不将条件变量与其他逻辑(例如,维护线程池)混合在一起。
写一个线程安全的队列。它所做的只是维护一个队列,并在有数据要读取时通知消费者。
最简单的一个有 3 个成员变量 -- 一个互斥量、一个条件变量和一个标准队列。
然后用关闭功能来增强它——现在 pop 必须返回一个可选的或有一些其他的失败路径。
您的任务需要先对任务进行批处理,然后再将它们全部解雇。你确定你想要那个吗?为此,我要做的是在线程安全队列中添加一个“推送多个任务”接口(interface)。然后在非线程安全队列中维护“未就绪”任务,并且仅在我们希望线程使用它们时才将它们全部推送。
“线程池”然后消费线程安全队列。因为我们单独编写了线程安全队列,所以移动部件的数量减少了一半,这意味着关系减少了 4 倍。
线程代码很难。尊重它。
关于c++ - std::condition_variable::notify_all() 只唤醒我的线程池中的一个线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57219650/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!