最简单的投票/同步算法

时间:2009-06-14 01:55:16

标签: algorithm concurrency synchronization voting linden-scripting-language

一个或多个人可以用来决定他们应该执行某项任务的最简单算法是什么?有一项任务,只需要完成一次,一个或多个人。人们可以说话,即彼此发送消息。通信必须是最小的,所有人都使用完全相同的算法。

一个人说“我正在做”并不够好,因为两个人可能同时说出来。

我想到的最简单的是,每个人都会说一个数字然后等一下。如果有人在那个时候回应,那么数量较少的人“赢”并完成任务。如果没有人回应,那么有人说她正在这样做而且做到了。当她说她这样做时,其他人都会退缩。这应该足以避免两个人在同一时间完成任务(因为有等待/握手期),但如果两个人说同一个号码,可能需要“第二轮”。

有什么更简单的东西吗?

对于那些好奇的人,我正在尝试同步几个SecondLife LSL脚本副本,只做一次。

9 个答案:

答案 0 :(得分:2)

这实际上取决于人们拥有的通信原语。如果有共享内存,compare-and-swap是一个很好的,简单的方法 - 每个人都只是尝试用1代替0,而任何成功的人都可以完成任务。

如果你只有消息发送,你可能需要实现Paxos协议或类似的东西。在这种情况下,要非常小心,你可以证明你的协议的正确性,因为它比它看起来更微妙!

编辑:既然你说你正在使用LSL,为什么不让他们使用LlHTTPRequest查询外部服务器进行仲裁?如果您担心托管外部服务器的位置,可以使用App Engine或其他类似的东西。

答案 1 :(得分:1)

大多数语言都有一个原子增量命令。将变量初始化为0.每个人都将变量递增1.然后线程可以使用它的增量的个人返回值来查找它是哪一个。因此,如果您只需要执行一个操作,则可能是数字0。

整个排序仍可用于更复杂的操作,例如让一半的线程做一件事,另一半等等。

编辑:你说“人”,但我认为你的意思是线程,因为你的最后一句话说这是由脚本完成的。

答案 2 :(得分:1)

好的,这是一个很长的镜头。也许,它将有助于制定一些方法。

假设。

  • 您可以在计算机之间进行通信(LSL实例)
  • 你有一个任务生成点,可以将任务发布为所有实例的待完成任务

  • 如果您可以创建可供所有实例使用的某种类型的列表
  • 任务生成器可以创建列表实例(或在列表中输入需求条目)
  • 其他实例检测列表(或其中的新条目)
  • 需要一段时间才能获得需要回复的实例
  • 实例可以将其ID放在列表中以表明他们对完成任务的兴趣
  • 超时后,最后一个回答是选择的那个(或者,根据你的动态,你可以选择列表中的第一个);我假设发电机此时发布了对该实例的接受
  • 如果所有实例都能看到列表,那么正确的人就知道选举何时完成

个别实例的反应时间及其可用性应该可以完成您的工作

答案 3 :(得分:1)

抽奖。

每个人都得到一个号码......它可能是一个座位号,也可能是一个票根号码。

将数字放入帽子中,拉出一个并让它们站起来。

这也可以扩展,即使是数量庞大,速度也更快。缺点是它确实需要数字分配前提。

答案 4 :(得分:1)

我想说答案取决于你是否能够发送广播信息。

如果你有能力发送广播,你只需等待一小段(10ms)的随机时间,发送广播,等待一段合理的时间以允许网络延迟,然后让发送广播的人最早的消息完成任务。

如果您的网络只知道它的邻居,那么你会做同样的事情但是轮流;在每一轮中,你会消除一些继续做不同的节点。

实际上,我建议你选择“最早的时间”,而不是“最小的数字”,这是因为第一个应该与更快的机器/拥有良好的网络连接/闲置相关联,这就是质量您希望所选机器执行任务。

答案 5 :(得分:0)

让每个人排成一行。排队的下一个人得到了这份工作。

答案 6 :(得分:0)

好的,因为这些都是房间里的真人,答案变得简单,而且这是一种常见的解决方案。

岩石剪刀。

每个人都配对并玩RPS。为了方便起见,每组允许2名和3名玩家(尽管这使得赔率不完全均匀)。每轮比赛结束后,获胜者与获胜者配对并重复,直到您只有一名获胜者。

这实际上很好地扩展!我曾经在一个愚蠢的团队建设破冰船上工作,这个会议室在5分钟内就可以容纳500多人。

答案 7 :(得分:0)

最后,我想出了一个效果很好的解决方案。我开始思考 - 为什么不尽量减少碰撞(也就是说,试着只有一名志愿者),而不是谈判谁完成任务?

关键原则是:

  • 如果志愿者是唯一的志愿者,他就是那个做志愿者的人
  • 如果两个(或更多)人想要同时执行任务,请使两个人等待一段随机时间以最大限度地减少冲突

算法是:

  1. 如果你听到“完成了!”在任何时候,即使等待,重启
  2. 等待随机时间(30米至1小时30米)
  3. 等待不断预定义的时间(一个周期,24小时)
  4. 如果有任务要完成
    1. 说“我还活着!”
    2. 等待5秒(假设任务总是在不到5秒的时间内完成!)
    3. 如果你听到“我还活着!”在那段时间内
      1. 重复“我还活着!”
      2. 转到2
    4. 其他(如果你没有听到任何声音)
      1. 执行任务
      2. 说“完蛋!”
  5. 重新开始
  6. 这基本上意味着存在两种可能的信号/消息的语法(“我还活着!”和“完成!”)。

    说n是客户/人的数量。在理想条件下,当没有任何n的冲突时,这也意味着每个周期2个消息。当存在冲突时,这意味着每次冲突每个周期+2个消息。它的可能性很小,但在最坏的情况下,有n个消息加上在这个循环中没有完成任务。在最糟糕的情况下,任务完成,有n + 1条消息。

    可能的简化

    因为我可以跳过一个循环(任务在这个循环中没有完成),但我不能在下一个循环之前完成另一个任务,我让大家在开始时等待一个循环。如果您没有“每个周期一个任务”要求,则可以跳过步骤3.仍然无法保证任务将在一个周期内完成,但步骤2中的值较大,步骤4.2中的值较小,以及少数客户/人我们有很好的机会。

    即使只有一个信号/消息,算法也可以工作。我们可以跳过第1步并说“我还活着!”在4.4.2中也是如此,但只有4.4.1才能使步骤4中的检查立即失败。

答案 8 :(得分:0)

这是LSL原型实现(公共领域,但您可能需要根据自己的需要进行调整):

// user configuration parameters
integer CHANNEL = -635;

// ------------------------------------------------

// scripter configuration parameters (in seconds)
float POLL_PERIOD = 60.0;               // 1 minute
// minimum length of suspend period
float CORE_SUSPEND_PERIOD = 15.0;       // 15 seconds 
// maximum length added to core suspend period (minimum is 0)
float MAX_RANDOM_SUSPEND_PERIOD = 30.0; // 30 seconds
float LOCK_PERIOD = 5.0;                // 5 seconds

// variables
integer lock = FALSE;


// mock poll method, assumes there are always tasks
integer poll()
{
    llSay(0, "Polling for tasks...");
    return TRUE;
}

// mock work method
work()
{
    llSay(0, "*** Executing task... ***");
}

default
{
    state_entry()
    {
        llSay(0, "Entering default state.");
        lock = FALSE;
        llListen(CHANNEL, "", NULL_KEY, "");
        llSetTimerEvent(POLL_PERIOD);
    }

    timer()
    {
        if (lock)
        {
            // step 4 - do some work
            work();
            // step 5 - make everybody go into suspend state
            // to make sure run times are randomized AND not sooner
            // than POLL_PERIOD
            llRegionSay(CHANNEL, "suspend");
            lock = FALSE;
            state suspended;
        }
        else
        {
            if (poll())
            {
                // step 1 - acquire lock
                llRegionSay(CHANNEL, "lock");
                lock = TRUE;
                // step 2 - wait and listen for others
                llSetTimerEvent(LOCK_PERIOD);
            }
        }
    }

    listen(integer channel, string name, key id, string message) 
    {
        // step 3 - did someone reply?
        if (message == "lock" && lock)
        {
            // other script woke up at the same time - signal
            // that you're here and suspend until next round,
            // where there will hopefully be a winner
            llSay(0, "Collision!");
            llRegionSay(CHANNEL, "lock");
            state suspended;
        }
        else if (message == "suspend")
            state suspended;
    }
}

state suspended
{
    state_entry()
    {
        // this gives random number between 0 and MAX_RANDOM_SUSPEND_PERIOD
        float random = llFrand(MAX_RANDOM_SUSPEND_PERIOD);
        float total = CORE_SUSPEND_PERIOD + random;
        llSetTimerEvent(total);
        llSay(0, "Entering suspended state for " + (string) ((integer) total)
            + " seconds.");
    }

    timer()
    {
        state default;
    }
}