基于共享字典数据结构的并发队列(单个使用者,多个生成者)的算法

时间:2015-10-14 01:10:35

标签: algorithm concurrency queue

我正在寻找一种满足以下属性的队列算法:

  1. 进程仅使用共享字典(键值存储)进行通信
  2. 除了加载和存储(例如,没有CAS)
  3. 之外,不使用任何原子操作
  4. 支持多个制作人
  5. 支持单个消费者
  6. 生产者可以随时死亡,队列必须保持运作
  7. 消费者也可以随时死亡并在以后重新启动,但一次只能运行多个消费者流程
  8. 这是一个关于合适算法的一般问题,因为我想在几个不同的场景中使用它。但是为了帮助可视化需求,下面是一个用例示例:

    • 我的网站有两个页面:producer.html和consumer.html
    • producer.html可以同时在多个标签中打开
    • 每个producer.html将事件添加到队列
    • 一个consumer.html的副本已打开并使用这些事件(例如,将它们聚合并流式传输到网络服务器)

    如果用户而不是页面打开了多个producer-tabs,则这些选项卡没有彼此可用的引用,因此通常的通信方法(postMessage或直接调用其他选项卡的JS代码) ) 出局。他们之间仍可以相互沟通的方式之一是LocalStorage,如下所示:Javascript; communication between tabs/windows with same origin。但是LocalStorage不是线程安全的#34;详细here

    注意:可能还有其他方法可以在浏览器中实现交叉标签通信(Flash,...),但这些这个问题的目的是因为他们不会转换为我的其他用例。这实际上只是我想要找到的一般队列算法的一个示例用例。

    更多参数:

    • 生产者的数量永远不会很大(可能是10或100),因此,生产者数量所需的读写次数的缩放并不是真正的问题。
    • 我事先并不知道我可能拥有多少生产者,并且没有立即明显的方法来为他们分配数字或索引。 (许多互斥算法(Lamport's BakeryEisenberg&McGuireSzymański's,...)为每个进程维护一个状态数组,这在这里不一定是一种自然的方法,尽管如果可以某种方式使用共享字典实现这些方法,我不想事先排除这些方法......)
    • 算法应该100%可靠。所以,我想避免Lamport's first Fast Mutex algorithm (page 2 in the PDF)之类的延迟,因为我没有任何实时保证。
    • 如果队列是FIFO,那将非常有用,但并非严格要求。
    • 该算法不应受任何专利等的妨碍。

    更新

    Two-Lock Concurrent Queue Algorithm by Michael and Scott看起来可行,但我需要两件事来实现它:

    • 使用共享字典的锁定机制,可以在锁定器的崩溃中幸存下来
    • 分配新节点的可靠方法(如果我将分配移动到锁定区域,我可以生成新的随机密钥,直到我找到一个尚未使用的密钥,但可能有更好的方法?)

    更新2:

    看来,我对这本字典还不够具体:

    它只不过是一个微不足道的键值存储。它提供了函数get(key)来读取键的值,put(key, value)来更改键的值,delete(key)来删除键。在我的一些用例中,我也可以迭代密钥,但如果可能的话,我希望避免它的普遍性。密钥是任意的,生产者和消费者可以根据需要创建或计算它们。该词典不提供任何自动生成唯一键的工具。

    示例包括HTML LocalStorage,Google AppEngine的数据存储区,Java Map,Python字典,甚至只包含单个目录的文件系统(其中键是文件名,值是内容的值)的文件)。

1 个答案:

答案 0 :(得分:0)

只需使用RDBMS即可。在MS SQL中非常简单,对于PostgresSQL,您必须使用RETURNING关键字,对于MySQL,您可能必须使用触发器。

CREATE TABLE Q ([Key] BIGINT IDENTITY(1,1) PRIMARY KEY, [Message] NVARCHAR(4000))

INSERT INTO Q OUTPUT inserted.* VALUE(@message)

DELETE TOP(1) Q WITH (READPAST) OUTPUT deleted.*

如果您真的希望使用算法解决方案,只需使用环形缓冲区。

const int MAX_Q_SIZE = 20000000;
static string[] Q = new string[MAX_Q_SIZE];
static long ProducerID = 0;
static long ConsumerID = 0;
public static long Produce(string message) {
    long key = Interlocked.Increment(ref ProducerID);
    int idx = (int)(key % MAX_Q_SIZE);
    Q[idx] = message;
    return key;
}
public static string Consume() {
    long key = Interlocked.Increment(ref ConsumerID);
    int idx = (int)(key % MAX_Q_SIZE);
    string message = Q[idx];
    return message;
}