Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading “什么是”呢;线性化能力;?_Multithreading_Concurrency_Parallel Processing_Thread Safety - Fatal编程技术网

Multithreading “什么是”呢;线性化能力;?

Multithreading “什么是”呢;线性化能力;?,multithreading,concurrency,parallel-processing,thread-safety,Multithreading,Concurrency,Parallel Processing,Thread Safety,有人能帮我理解什么是线性化能力吗?我需要一个简单易懂的解释。我正在阅读Maruice Herilihy和Nir Shavit的《多处理器编程艺术》,并试图理解第3章关于并发对象的内容 我理解,如果从其他线程的角度来看,一个方法在某个点上似乎会瞬间“生效”,那么它是可线性化的。这是有道理的,但也有人说,线性化实际上是执行历史的一个属性。执行历史是可线性化的,这意味着什么?我为什么关心它?它与可线性化的方法或对象有什么关系 谢谢大家! 如果 (a) 它的每一种方法都是原子的。将它们想象成java同步

有人能帮我理解什么是线性化能力吗?我需要一个简单易懂的解释。我正在阅读Maruice Herilihy和Nir Shavit的《多处理器编程艺术》,并试图理解第3章关于并发对象的内容

我理解,如果从其他线程的角度来看,一个方法在某个点上似乎会瞬间“生效”,那么它是可线性化的。这是有道理的,但也有人说,线性化实际上是执行历史的一个属性。执行历史是可线性化的,这意味着什么?我为什么关心它?它与可线性化的方法或对象有什么关系


谢谢大家!

如果

(a) 它的每一种方法都是原子的。将它们想象成java同步方法,下面将介绍更多内容

(b) 任何给定线程/处理器最多只能有一个挂起的操作

(c) 操作必须在返回之前生效。对象让它们排队以懒散地执行它们是不可接受的

现在,(a)可以放宽很多。线性化要求该操作的效果是原子的。因此,无锁链表中的add操作将有一个执行点(“线性化点”),在该点之前没有添加元素,之后元素肯定处于状态。这比获得锁要好,因为锁可以无限期地阻塞

现在,当多个线程同时调用一个可线性化的对象时,该对象的行为就像是以某种线性顺序调用方法一样(因为原子性要求);两个重叠调用可以按任意顺序线性进行

而且,由于它们在方法调用期间的某个时间被迫产生影响(堆栈必须推送/弹出,集合必须添加/删除等),因此可以使用众所周知的顺序规范方法(前置和后置条件等)对对象进行推理


当我们在它,线性化和顺序一致性之间的区别是后者不需要(c)。对于顺序一致的数据存储,方法不必立即生效。换句话说,方法调用仅仅是对操作的请求,而不是操作本身。在可线性化对象中,方法调用是对操作的调用。可线性化对象是顺序一致的,但并非相反

可能混淆了线性化和串行化

发件人:

线性化能力是对单个对象上的单个操作的保证[…]读写操作的线性化能力与术语“原子一致性”同义,是吉尔伯特和林奇对CAP定理的证明中的“C”或“一致性”


可序列化性是对一个或多个对象上的事务或一个或多个操作组的保证。它保证在多个项目上执行一组事务(通常包含读写操作)相当于事务的某些串行执行(总顺序)[……]在ACID中,串行化是传统的“I”或隔离。

好吧,我想我可以简明扼要地回答这个问题

当我们要判断并发对象是否正确时,我们总是试图找到一种方法,将偏序扩展到总序

我们可以更容易地识别顺序对象是否正确

首先,让我们把并发对象放在一边。我们以后再讨论。现在让我们考虑一个序贯历史HYS,一个顺序历史是一系列事件(即调用和响应),每个调用都遵循相应的响应<强>即时< /强>。可能会被混淆,考虑单线程程序的执行,当然每个调用和它的响应之间有一个间隔,但是这些方法是逐个执行的,所以“瞬间”意味着没有其他调用/响应可以绑定到一对调用子集i响应子i)/p> H_S may看起来像:

H_S : I1 R1 I2 R2 I3 R3 ... In Rn
(Ii means the i-th Invoke, and Ri means the i-th Response) 
对历史H_S的正确性进行推理是很容易的,因为没有任何并发性,我们需要做的是检查执行是否如我们预期的那样工作(满足顺序规范的条件)。换句话说,这是一个连续的历史

好的,事实是我们正在使用一个并发程序。例如,我们在程序中运行两个线程A和B。每次运行程序时,我们都会得到执行的历史记录H_C(history_Concurrent)。在HYS中,我们需要考虑一个方法调用II~RI。当然,在线程A和线程B调用的方法调用之间必须有很多重叠,但是每个事件(即调用和响应)都有它的实时顺序。因此,A和B调用的所有方法的调用和响应都可以映射到一个顺序,顺序可能如下所示:

H_C : IA1 IB1 RA1 RB1 IB2 IA2 RB2 RA2
顺序似乎有点混乱,它只是每个方法调用的事件类型:

thread A:         IA1----------RA1               IA2-----------RA2
thread B:          |      IB1---|---RB1    IB2----|----RB2      |
                   |       |    |    |      |     |     |       |
                   |       |    |    |      |     |     |       |
real-time order:  IA1     IB1  RA1  RB1    IB2   IA2   RB2     RA2
                ------------------------------------------------------>time
我们拿到了H_C。 那么,我们如何检查H_C的执行是否正确呢?我们可以按照以下规则将H_C重新排序为H_RO:

规则:如果一个方法调用m1先于另一个m2,那么在重新排序的序列中,m1必须先于m2。(这意味着如果Ri在H_C中位于Ij之前,你必须保证Ri在重新排序的序列中仍然位于Ij之前,i和j没有它们的顺序,我们也可以使用a,b,C…)我们说在这种规则下H_C与H_RO(历史重新排序)是等价的

H_RO将有两个属性:

  • 它尊重程序顺序
  • 它保持了实时性
  • 在不违反上述规则的情况下重新排序H_C,我们可以得到一些连续的历史记录
    H_S1: IA1 RA1 IB1 RB1 IB2 RB2 IA2 RA2
    H_S2: IB1 RB1 IA1 RA1 IB2 RB2 IA2 RA2
    H_S3: IB1 RB1 IA1 RA1 IA2 RA2 IB2 RB2
    H_S4: IA1 RA1 IB1 RB1 IA2 RA2 IB2 RB2
    
    H_S5: IA1 RA1 IA2 RA2 IB1 RB1 IB2 RB2
    
    //int i = 0; i is a global shared variable.
    int inc_counter() {
        int j = i++;
        return j;
    }
    
    #Pseudo-asm-code
    Load   register, address of i
    Add    register, 1
    Store  register, address of i
    
    thread A:         IA1----------RA1(1)                  IA2------------RA2(3)
    thread B:          |      IB1---|------RB1(1)    IB2----|----RB2(2)    |
                       |       |    |        |        |     |     |        |
                       |       |    |        |        |     |     |        |
    real-time order:  IA1     IB1  RA1(1)  RB1(1)    IB2   IA2   RB2(2)   RA2(3)
                    ---------------------------------------------------------->time
    
    //int i = 0; i is a global shared variable.
    int inc_counter(){
        //do some unrelated work, for example, play a popular song.
        lock(&lock);
        i++;
        int j = i;
        unlock(&lock);
        //do some unrelated work, for example, fetch a web page and print it to the screen.
        return j;
    }
    
    thread A:         IA1----------RA1(2)                 IA2-----------RA2(4)
    thread B:          |      IB1---|-------RB1(1)    IB2--|----RB2(3)    |
                       |       |    |        |         |   |     |        |
                       |       |    |        |         |   |     |        |
    real-time order:  IA1     IB1  RA1(2)  RB1(1)     IB2 IA2   RB2(3)   RA2(4)
    
    IB1 RB1(1) IA1 RA1(2) IB2 RB2(3) IA2 RA2(4)  //a legal sequential history
    
    //top is the tio
    void push(T val) {
        while (1) {
            Node * new_element = allocte(val);
            Node * next = top->next;
            new_element->next = next;
            if ( CAS(&top->next, next, new_element)) {  //Linearization point1
                //CAS success!
                //ok, we can do some other work, such as go shopping.
                return;
            }
            //CAS fail! retry!
        }
    }
    
    T pop() {
        while (1) {
            Node * next = top->next;
            Node * nextnext = next->next;
            if ( CAS(&top->next, next, nextnext)) { //Linearization point2
                //CAS succeed!
                //ok, let me take a rest.
                return next->value;
            }
            //CAS fail! retry!
        }
    }