- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
考虑以下代码
#include <thread>
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
template <typename T>
class Tqueue
{
public:
Tqueue() : m_next_ticket(0),
m_counter(0) {}
void push(const T& e){
std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(e);
lock.unlock();
m_cond.notify_all();
};
T wait_and_pop() {
std::unique_lock<std::mutex> lock(m_mutex);
int ticket = m_next_ticket++;
m_cond.wait(lock,[=]{return (!m_queue.empty())
&& (ticket == m_counter);});
m_counter++;
T data = m_queue.front();
m_queue.pop();
return data;
}
private:
int m_next_ticket;
int m_counter;
std::queue<T> m_queue;
std::mutex m_mutex;
std::condition_variable m_cond;
};
这应该是我想出的公平队列的模板。在这种情况下,公平意味着 wait_and_pop() 调用返回的顺序与不同线程调用它们的顺序相同。
例如:线程 1 在空队列上调用 wait_and_pop() 并阻塞。然后 Thread 2 在空队列上调用 wait_and_pop() 并阻塞。之后 Thread 3 使用 push() 推送两个事件。现在 Thread 1 应该在 Thread 2 之前返回。
使用以下代码,有时它会起作用。但大多数时候代码永远阻塞:
Tqueue<int> queue;
std::mutex mutex;
void test(int i)
{
auto bla = queue.wait_and_pop();
std::cout << "Thread : "<<bla << std::endl;
}
const int SIZE = 200;
int main(int argc, char *argv[])
{
std::vector<std::thread> threads;
for(int i = 0; i < SIZE; ++i)
threads.push_back(std::thread(test,i));
for(int i = 0; i < SIZE; ++i)
queue.push(i);
for(int i = 0; i < SIZE; ++i)
threads[i].join();
return 0;
}
想法是为每个线程创建一个唯一的票证。使用条件变量,然后我们在 wait_and_pop() 函数中等待,直到插入一个新事件。在 push() 函数中,新事件被插入队列并通知所有等待线程。每个线程检查是否队列不再为空并且唯一票证等于当前柜台。如果是这样,特定线程离开条件循环,从队列中弹出当前事件并增加计数器。
我怀疑有些通知丢失了,但我无法理解为什么会这样。有什么想法可以解决这个问题或如何以正确的方式实现这个问题吗?
编辑我如下更改了队列中的代码。现在它似乎工作。重要的是,我通知,同时仍然持有锁(在 push() 和 wait_and_pop() 中)。此外,我将票证系统更改为线程 ID 队列,但这只是为了方便,它使源代码保持紧凑。但我不确定,如果我想在生产代码,因为我不明白为什么它现在可以工作,而且我不知道它是否适用于所有情况。也许有人可以对此发表评论?
template <typename T>
class Tqueue
{
public:
void push(const T& e){
std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(e);
m_cond.notify_all();
};
T wait_and_pop() {
std::unique_lock<std::mutex> lock(m_mutex);
m_ids.push(std::this_thread::get_id());
m_cond.wait(lock,[=]{return (!m_queue.empty())
&& (m_ids.front() == std::this_thread::get_id());});
T data = m_queue.front();
m_queue.pop();
m_ids.pop();
m_cond.notify_all();
return data;
}
private:
std::queue<T> m_queue;
std::queue<std::thread::id> m_ids;
std::mutex m_mutex;
std::condition_variable m_cond;
};
最佳答案
通知确实丢失了。有可能许多 push
生成更少数量的线程被唤醒,因为当 m_cond.notify_all();
被执行时,它只是让等待线程 runnable,即准备运行。这些线程仍然需要等待并获取 m_cond.wait
中的锁。
也有可能是主线程在单个 等待线程最终可以执行之前继续多次获取互斥量。这导致通知饥饿。
为了使该机制发挥作用,您需要在条件受到影响时随时通知。您已经通知 m_queue.push(e);
,这会影响第一个条件 !m_queue.empty()
。您还需要在 wait_and_pop
结束时通知,以处理第二个条件 ticket == m_counter
。
T wait_and_pop() {
....blah blah
T data = m_queue.front();
m_queue.pop();
lock.unlock();
m_cond.notify_all();
return data;
}
注意:有可能
在这里我的意思是“最终会有一个最终发生的线程调度”。我不是说“我不确定”。
进一步说明:condition_variable.notify_all()
只保证最终唤醒线程。它不保证 X
次调用会唤醒 X
次。此外,由于您的情况,减少了保证只通知一个线程,这是根本原因。
关于在wait_and_pop解锁前后通知
在 wait_and_pop
中释放锁之前或之后通知应该没有任何区别。我指定的修改应该与编辑中的修改一样。我一直在进行一些变化(线程数、等待 x 线程完成并再次推送)的测试,结果相同。
关于c++ - 公平队列丢失通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32332926/
我遇到一种情况,我需要从某个主题读取(正在进行的)消息并将它们放入另一个 Queue 中。我怀疑我是否需要 jms Queue 或者我可以对内存中的 java Queue 感到满意。我将通过同一 jv
队列也是一种操作受限的线性数据结构,与栈很相似。 01、定义 栈的操作受限表现为只允许在队列的一端进行元素插入操作,在队列的另一端只允许删除操作。这一特性可以总结为先进先出(First In
队列的定义 队列(Queue):先进先出的线性表 队列是仅在队尾进行插入和队头进行删除操作的线性表 队头(front):线性表的表头端,即可删除端 队尾(rear):线性表的表尾端,即可插入端 由于这
Redis专题-队列 首先,想一想 Redis 适合做消息队列吗? 1、消息队列的消息存取需求是什么?redis中的解决方案是什么? 无非就是下面这几点: 0、数据可以顺序读
0. 学习目标 栈和队列是在程序设计中常见的数据类型,从数据结构的角度来讲,栈和队列也是线性表,是操作受限的线性表,它们的基本操作是线性表操作的子集,但从数据类型的角度来讲,它们与线性表又有着巨大的不
我想在 redis + Flask 和 Python 中实现一个队列。我已经用 RQ 实现了这样的查询,如果你有 Flask 应用程序和任务在同一台服务器上工作,它就可以正常工作。我想知道是否有可能创
我正在使用 Laravel 5.1,我有一个大约需要 2 分钟来处理的任务,这个任务特别是生成报告...... 现在,很明显,我不能让用户在我接受用户输入的同一页面上等待 2 分钟,而是我应该在后台处
我正在使用 Azure 队列,并且有多个不同的进程从队列中读取数据。 我的系统的构建方式假设每条消息只读取一次。 这个Microsoft article声称 Azure 队列具有至少一次传送保证,这可
我正在创建一个Thread::Queue元素数组。 我这样做是这样的: for (my $i=0; $i new; } 但是,当我在每个队列中填充这样的元素时 $queues[$index]->enq
我试图了解如何将我的 Mercurial 补丁推送到远程存储库(例如 bitbucket.org),而不必先应用它们(实际上提交它们)。我的动机是在最终完成之前首先对我的工作进行远程备份,并且能够与其
我的本地计算机上有一个 Mercurial 队列补丁,我需要与同事共享该补丁,但我不想将其提交到上游存储库。有没有一种简单的方法可以打包该补丁并与他分享? 最佳答案 mq 将补丁作为不带扩展名的文
Java 中是否有任何类提供与 Queue 相同的功能,但有返回对象的选项,并且不要删除它,只需将其设置在集合末尾? 最佳答案 Queue不直接提供这样的方法。但是,您可以使用 poll 和 add
我在Windows上使用Tortoise svn客户端,我需要能够一次提交来自不同子文件夹的更改文件-一次提交。像在提交之前将文件添加到队列中之类的?我该怎么做? Windows上是否还有另一个svn
好吧,我正在尝试对我的 DSAQueue 类进行单元测试,它显示我的 isEmpty()、isFull() 和 dequeue() 方法失败。 以下是我的 DSAQueue 代码。我认为我的 Dequ
我想尽量减少对传入请求的数据库查询。它目前需要写入 6 个不同的表。在返回响应之前不需要完成处理。因此,我考虑了 laravel 队列,但我想知道我是否也可以摆脱写入队列/作业表所需的单独查询。我可以
我正在学习队列数据结构。我想用链表创建队列。我想编程输出:10 20程序输出:队列为空-1 队列为空-1 我哪里出错了? 代码如下: class Node { int x; Node next
“当工作人员有空时,他们会根据主题的优先级列表从等待请求池中进行选择。在时间 t 到达的所有请求都可以在时间 t 进行分配。如果两名工作人员同时有空,则安排优先权分配给最近的工作最早安排的人。如果仍然
我正在开发一个巨大的应用程序,它使用一些子菜单、模式窗口、提示等。 现在,我想知道在此类应用程序中处理 Esc 和单击外部事件的正确方法。 $(document).keyup(function(e)
所以 如果我有一个队列 a --> b --> NULL; 当我使用函数时 void duplicate(QueueNodePtr pHead, QueueNodePtr *pTail) 它会给 a
我正在尝试为键盘输入实现 FIFO 队列,但似乎无法让它工作。我可以让键盘输入显示在液晶显示屏上,但这就是我能做的。我认为代码应该读取键盘输入并将其插入队列,然后弹出键盘输入并将值读取到液晶屏幕上。有
我是一名优秀的程序员,十分优秀!