【问题标题】:Can timestamp be used in synchronization of processes having Race Condition?时间戳可以用于同步具有竞争条件的进程吗?
【发布时间】:2019-11-24 11:33:17
【问题描述】:

我想知道 timestamp 是否可以用于解决进程同步问题,当竞争条件发生时?
下面是 entry 的算法>exit section为每个想要进入临界区的进程。入口部分使用 FCFS(先到先服务)技术来提供对关键部分的访问权限。

    interested[N] is shared array of N integers where N is number of processes.

    // This section executed when process enters critical section.
    entry_section (int processNumber) {
        interested [processNumber] = getCurrentTimeStamp ();  // Gets the current timestamp.
        index = findOldestProcessNumber (interested);         // Find the process number with least timestamp.
        while (index != processNumber);
    }

    // This section executed when process leaves critical section.
    exit_section (int processNumber) {
        interested [processNumber] = NULL;
    }

在我看来,这个算法满足同步的所有条件,即互斥、进度、有界等待和可移植性。那么,我说的对吗?

感谢您抽出宝贵时间。

【问题讨论】:

  • 您在测试时发现了什么?
  • @MartinJames 我没有用任何语言实现它,但我在想这是否可以做到?
  • @MartinJames 对于 2 个进程有一个很好的解决方案,即满足问题中提到的所有条件的彼得森算法,但我认为这个解决方案也可以适用于 2 个以上的进程。
  • 为什么有人对这个问题投了反对票?问这种类型的问题是不是不对?
  • @Shiv - 我只是想知道,您的流程共享哪些数据?如果两个或多个进程在完全相同的时间戳使用共享数据会发生什么?如果由于某种原因两个或多个进程以相同的时间戳开始会发生什么?这可能会发生,因为 2Ghz CPU 每秒可以执行 20 亿个周期。所以从技术上讲,多个操作可以在同一毫秒内发生。因此,如果您的时间戳只达到秒,那么这个问题将变得最糟糕。

标签: process operating-system synchronization


【解决方案1】:

简短而甜蜜,这是这种方法的两个问题。

  1. 您的所有进程都在忙于等待。这意味着即使进程不能进入临界区,它仍然不能休息。这意味着 os 调度程序需要不断地调度所有感兴趣的进程,即使它们没有产生有意义的输出。这会损害性能和功耗。
  2. 这是最大的一个。不能保证两个进程不会有相同的时间戳。这可能不太可能,但当您想要保证互斥以防止竞争条件时,可能性并不是您要寻找的。​​li>

【讨论】:

    【解决方案2】:

    您的代码只是一个草图,但很可能不会在所有情况下都有效。

    如果没有锁并且所有函数都使用非原子操作,则无法保证代码将正确执行。它与第一个示例here 基本相同,只是您使用的是数组并假设您不需要原子性,因为每个进程只会访问自己的元素。

    让我试着想出一个反例。

    一些小的澄清。

    据我了解,每个进程的省略部分都在循环中运行

    while(!processExitCondition)
    {
        // some non-critical code
        ...
        // your critical section as in the question
        entry_section (int processNumber) {
            interested [processNumber] = getCurrentTimeStamp ();  // Gets the current timestamp.
            index = findOldestProcessNumber (interested);         // Find the process number with least timestamp.
            while (index != processNumber);
        }
    
        // This section executed when process leaves critical section.
        exit_section (int processNumber) {
            interested [processNumber] = NULL;
        }
        // more non-critical code
        ...
    }
    

    在我看来,调度部分应该是忙于等待,不断获取最旧的进程:

    while (findOldestProcessNumber (interested) != processNumber);
    

    否则,您的所有线程都可以立即挂在无限 while 循环中,除了第一个将执行一次并在此之后立即挂起的线程。

    现在您的调度函数findOldestProcessNumber (interested); 有一些有限的执行时间,如果我对存在进程外循环while(!processExitCondition) 的假设正确,那么这个执行时间可能会比内部、之前或之后的代码执行时间要慢临界区。结果,完成的进程可以在findOldestProcessNumber (interested); 迭代之前返回到interested 数组,如果getCurrentTimeStamp (); 的保真度低(比如秒),您可以让两个进程同时进入临界区。想象一下在findOldestProcessNumber (interested); 中添加一个长时间的睡眠,这样会更容易看出这是如何发生的。

    您可以说这是一个人为的示例,但关键是无法保证进程将如何相互交错,因此您的同步依赖于代码的某些部分执行特定时间“大”的假设或足够“小”。这只是尝试使用这些假设来伪造原子操作。

    您可以提出相反的想法以使其发挥作用。假设您可以实现getCurrentTimeStamp () 为每个调用者返回一个唯一的时间戳。一个简单的带有硬件的原子计数器保证只有一个进程可以增加它,或者在内部使用原子锁(互斥锁),它是自己的关键部分,如果你愿意,它会忙着等待这个锁为每个调用者进程提供一个不同的系统时钟值让它成为实时的。但是通过单独的findOldestProcessNumber (interested) 电话,我发现很难想出一种方法来保证它。我不能说这是不可能的,但它越复杂,你就越有可能只是隐藏缺乏互斥保证。

    因此,使用锁(互斥锁)的最简单解决方案是最好的。在您的代码中 sn-p 在您的关键部分周围添加一个互斥锁,您当前的进入和退出代码仅用于按先到先得的原则进行调度,而互斥锁则为您提供互斥保证。

    如果您想要一个无锁解决方案,您可以使用Boost lockfree 队列或实现一个无锁ring buffer 将进程号推送到entry_section 中的队列,然后等待它轮到它,尽管performance can be worse 作为控制- 看起来很直观。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-04
      • 2010-09-29
      • 1970-01-01
      • 2021-06-04
      • 1970-01-01
      • 1970-01-01
      • 2023-01-30
      • 2018-08-21
      相关资源
      最近更新 更多