Linux下的多线程同步(一)


一.探讨线程非同步

1.1线程不同步会发生什么

进行多线程编程,如果多个线程需要对同一块内存进行操作,比如:同时读、同时写、同时读写对于后两种情况来说,如果不做任何的人为干涉就会出现各种各样的错误数据。这是因为线程在运行的时候需要先得到 CPU 时间片,时间片用完之后需要放弃已获得的 CPU 资源,就这样线程频繁地在就绪态和运行态之间切换,更复杂一点还可以在就绪态、运行态、挂起态之间切换,这样就会导致线程的执行顺序并不是有序的,而是随机的混乱的,就如同下图中的这个例子一样,理想很丰满现实却很残酷;
在这里插入图片描述
下面,我来介绍一下线程同步的例子:
假设有 4 个线程 A、B、C、D,当前一个线程 A 对内存中的共享资源进行访问的时候,其他线程 B, C, D 都不可以对这块内存进行操作,直到线程 A 对这块内存访问完毕为止,B,C,D 中的一个才能访问这块内存,剩余的两个需要继续阻塞等待,以此类推,直至所有的线程都对这块内存操作完毕。 线程对内存的这种访问方式就称之为线程同步,通过对概念的介绍,我们可以了解到所谓的同步并不是多个线程同时对内存进行访问,而是按照先后顺序依次进行的。
注意:一定是大家同一时间访问时,才需要排队处理;(就好像银行的柜台,只有这段时间来的顾客才需要排队处理,否则就是单线程的处理)

请看下列程序分析:(这样我们就知道为什么需要线程同步了)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>

#define MAX 50
// 全局变量
int number;

// 线程处理函数
void* funcA_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        int cur = number;
        cur++;
        usleep(10);
        number = cur;
        printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
    }

    return NULL;
}

void* funcB_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        int cur = number;
        cur++;
        number = cur;
        printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
        usleep(5);
    }

    return NULL;
}

int main(int argc, const char* argv[])
{
    pthread_t p1, p2;

    // 创建两个子线程
    pthread_create(&p1, NULL, funcA_num, NULL);
    pthread_create(&p2, NULL, funcB_num, NULL);

    // 阻塞,资源回收
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);

    return 0;
}

编译并执行上面的测试程序,得到如下结果:

$ ./a.out 
Thread B, id = 140504473724672, number = 1
Thread B, id = 140504473724672, number = 2
Thread A, id = 140504482117376, number = 2
Thread B, id = 140504473724672, number = 3
Thread A, id = 140504482117376, number = 4
Thread B, id = 140504473724672, number = 5
Thread A, id = 140504482117376, number = 6
Thread B, id = 140504473724672, number = 7
Thread B, id = 140504473724672, number = 8
Thread A, id = 140504482117376, number = 7
Thread B, id = 140504473724672, number = 8
Thread B, id = 140504473724672, number = 9
Thread A, id = 140504482117376, number = 8
Thread B, id = 140504473724672, number = 9
Thread A, id = 140504482117376, number = 9
Thread B, id = 140504473724672, number = 10
Thread B, id = 140504473724672, number = 11
Thread A, id = 140504482117376, number = 10
Thread B, id = 140504473724672, number = 11
Thread A, id = 140504482117376, number = 11
Thread B, id = 140504473724672, number = 12
Thread A, id = 140504482117376, number = 13
Thread B, id = 140504473724672, number = 14
Thread A, id = 140504482117376, number = 15
Thread B, id = 140504473724672, number = 16
Thread B, id = 140504473724672, number = 17
Thread B, id = 140504473724672, number = 18
Thread B, id = 140504473724672, number = 19
Thread A, id = 140504482117376, number = 17
Thread B, id = 140504473724672, number = 18
Thread B, id = 140504473724672, number = 19
Thread A, id = 140504482117376, number = 19
Thread B, id = 140504473724672, number = 20
Thread A, id = 140504482117376, number = 20
Thread B, id = 140504473724672, number = 21
Thread A, id = 140504482117376, number = 21
Thread B, id = 140504473724672, number = 22
Thread A, id = 140504482117376, number = 22
Thread B, id = 140504473724672, number = 23
Thread A, id = 140504482117376, number = 23
Thread B, id = 140504473724672, number = 24
Thread A, id = 140504482117376, number = 24
Thread B, id = 140504473724672, number = 25
Thread A, id = 140504482117376, number = 25
Thread B, id = 140504473724672, number = 26
Thread A, id = 140504482117376, number = 26
Thread B, id = 140504473724672, number = 27
Thread A, id = 140504482117376, number = 27
Thread B, id = 140504473724672, number = 28
Thread A, id = 140504482117376, number = 28
Thread B, id = 140504473724672, number = 29
Thread A, id = 140504482117376, number = 29
Thread B, id = 140504473724672, number = 30
Thread A, id = 140504482117376, number = 30
Thread B, id = 140504473724672, number = 31
Thread A, id = 140504482117376, number = 31
Thread B, id = 140504473724672, number = 32
Thread A, id = 140504482117376, number = 32
Thread B, id = 140504473724672, number = 33
Thread A, id = 140504482117376, number = 33
Thread B, id = 140504473724672, number = 34
Thread A, id = 140504482117376, number = 34
Thread B, id = 140504473724672, number = 35
Thread A, id = 140504482117376, number = 35
Thread B, id = 140504473724672, number = 36
Thread A, id = 140504482117376, number = 36
Thread B, id = 140504473724672, number = 37
Thread A, id = 140504482117376, number = 37
Thread B, id = 140504473724672, number = 38
Thread A, id = 140504482117376, number = 38
Thread B, id = 140504473724672, number = 39
Thread A, id = 140504482117376, number = 39
Thread A, id = 140504482117376, number = 40
Thread B, id = 140504473724672, number = 41
Thread B, id = 140504473724672, number = 42
Thread A, id = 140504482117376, number = 42
Thread A, id = 140504482117376, number = 43
Thread B, id = 140504473724672, number = 44
Thread B, id = 140504473724672, number = 45
Thread A, id = 140504482117376, number = 45
Thread B, id = 140504473724672, number = 46
Thread A, id = 140504482117376, number = 46
Thread B, id = 140504473724672, number = 47
Thread A, id = 140504482117376, number = 47
Thread B, id = 140504473724672, number = 48
Thread A, id = 140504482117376, number = 48
Thread B, id = 140504473724672, number = 49
Thread A, id = 140504482117376, number = 50
Thread B, id = 140504473724672, number = 51
Thread A, id = 140504482117376, number = 51
Thread B, id = 140504473724672, number = 52
Thread A, id = 140504482117376, number = 53
Thread A, id = 140504482117376, number = 54
Thread A, id = 140504482117376, number = 55
Thread A, id = 140504482117376, number = 56
Thread A, id = 140504482117376, number = 57
Thread A, id = 140504482117376, number = 58
Thread A, id = 140504482117376, number = 59
Thread A, id = 140504482117376, number = 60
Thread A, id = 140504482117376, number = 61

通过对上面例子的测试,可以看出虽然每个线程内部循环了 50 次每次数一个数,但是最终没有数到 100,通过输出的结果可以看到,有些数字被重复数了多次,其原因就是没有对线程进行同步处理,造成了数据的混乱
两个线程在数数的时候需要分时复用 CPU 时间片,并且测试程序中调用了 sleep() 导致线程的 CPU 时间片没用完就被迫挂起了,这样就能让 CPU 的上下文切换(保存当前状态,下一次继续运行的时候需要加载保存的状态)更加频繁,更容易再现数据混乱的这个现象。

在这里插入图片描述
CPU 对应寄存器、一级缓存、二级缓存、三级缓存是独占的,用于存储处理的数据和线程的状态信息,数据被 CPU 处理完成需要再次被写入到物理内存中,物理内存数据也可以通过文件 IO 操作写入到磁盘中。

在测试程序中两个线程共用全局变量 number 当线程变成运行态之后开始数数,从物理内存加载数据,让后将数据放到 CPU 进行运算,最后将结果更新到物理内存中。如果数数的两个线程都可以顺利完成这个流程,那么得到的结果肯定是正确的。

如果线程 A 执行这个过程期间就失去了 CPU 时间片,线程 A 被挂起了最新的数据没能更新到物理内存。线程 B 变成运行态之后从物理内存读数据,很显然它没有拿到最新数据,只能基于旧的数据往后数,然后失去 CPU 时间片挂起。线程 A 得到 CPU 时间片变成运行态,第一件事儿就是将上次没更新到内存的数据更新到内存,但是这样会导致线程 B 已经更新到内存的数据被覆盖,活儿白干了,最终导致有些数据会被重复数很多次。

1.2 同步方式

对于多个线程访问共享资源出现数据混乱的问题,需要进行线程同步。常用的线程同步方式有四种:互斥锁、读写锁、条件变量、信号量。所谓的共享资源就是多个线程共同访问的变量,这些变量通常为全局数据区变量或者堆区变量,这些变量对应的共享资源也被称之为临界资源
在这里插入图片描述
找到临界资源之后,再找和临界资源相关的上下文代码,这样就得到了一个代码块,这个代码块可以称之为临界区。确定好临界区(临界区越小越好)之后,就可以进行线程同步了,线程同步的大致处理思路是这样的:

a:在临界区代码的上边,添加加锁函数,对临界区加锁。
哪个线程调用这句代码,就会把这把锁锁上,其他线程就只能阻塞在锁上了。
b:在临界区代码的下边,添加解锁函数,对临界区解锁。
出临界区的线程会将锁定的那把锁打开,其他抢到锁的线程就可以进入到临界区了。
c:通过锁机制能保证临界区代码最多只能同时有一个线程访问,这样并行访问就变为串行访问了。

2. 互斥锁

互斥锁是线程同步最常用的一种方式,通过互斥锁可以锁定一个代码块,被锁定的这个代码块,所有的线程只能顺序执行 (不能并行处理),这样多线程访问共享资源数据混乱的问题就可以被解决了,需要付出的代价就是执行效率的降低,因为默认临界区多个线程是可以并行处理的,现在只能串行处理。

在 Linux 中互斥锁的类型为 pthread_mutex_t,创建一个这种类型的变量就得到了一把互斥锁:

pthread_mutex_t  mutex;

在创建的锁对象中保存了当前这把锁的状态信息:锁定还是打开,如果是锁定状态还记录了给这把锁加锁的线程信息(线程 ID)。一个互斥锁变量只能被一个线程锁定,被锁定之后其他线程再对互斥锁变量加锁就会被阻塞,直到这把互斥锁被解锁,被阻塞的线程才能被解除阻塞。一般情况下,每一个共享资源对应一个把互斥锁,锁的个数和线程的个数无关

Linux 提供的互斥锁操作函数如下,如果函数调用成功会返回 0,调用失败会返回相应的错误号:
// 初始化互斥锁
// restrict: 是一个关键字, 用来修饰指针, 只有这个关键字修饰的指针可以访问指向的内存地址, 其他指针是不行的
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
// 释放互斥锁资源            
int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数:
mutex: 互斥锁变量的地址
attr: 互斥锁的属性,一般使用默认属性即可,这个参数指定为 NULL

// 修改互斥锁的状态, 将其设定为锁定状态, 这个状态被写入到参数 mutex 中
int pthread_mutex_lock(pthread_mutex_t *mutex);

这个函数被调用,首先会判断参数 mutex 互斥锁中的状态是不是锁定状态:

  1. 没有被锁定,是打开的,这个线程可以加锁成功,这个这个锁中会记录是哪个线程加锁成功了
  2. 如果被锁定了,其他线程加锁就失败了,这些线程都会阻塞在这把锁上
  3. 当这把锁被解开之后,这些阻塞在锁上的线程就解除阻塞了,并且这些线程是通过竞争的方式对这把锁加锁,没抢到锁的线程继续阻塞
// 尝试加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);

调用这个函数对互斥锁变量加锁还是有两种情况:

  1. 如果这把锁没有被锁定是打开的,线程加锁成功
  2. 如果锁变量被锁住了,调用这个函数加锁的线程,不会被阻塞,加锁失败直接返回错误号
// 对互斥锁解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

不是所有的线程都可以对互斥锁解锁,哪个线程加的锁,哪个线程才能解锁成功。

2.1互斥锁的使用

我们可以将上面多线程交替数数的例子修改一下,使用互斥锁进行线程同步。两个线程一共操作了同一个全局变量,因此需要添加一互斥锁,来控制这两个线程。
我们来看下面的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>

#define MAX 100
// 全局变量
int number;

// 创建一把互斥锁
// 全局变量, 多个线程共享
pthread_mutex_t mutex;

// 线程处理函数
void* funcA_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        // 如果线程A加锁成功, 不阻塞
        // 如果B加锁成功, 线程A阻塞
        pthread_mutex_lock(&mutex);
        int cur = number;
        cur++;
        usleep(10);
        number = cur;
        pthread_mutex_unlock(&mutex);
        printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
    }

    return NULL;
}

void* funcB_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        // a加锁成功, b线程访问这把锁的时候是锁定的
        // 线程B先阻塞, a线程解锁之后阻塞解除
        // 线程B加锁成功了
        pthread_mutex_lock(&mutex);
        int cur = number;
        cur++;
        number = cur;
        pthread_mutex_unlock(&mutex);
        printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
        usleep(5);
    }

    return NULL;
}

int main(int argc, const char* argv[])
{
    pthread_t p1, p2;

    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 创建两个子线程
    pthread_create(&p1, NULL, funcA_num, NULL);
    pthread_create(&p2, NULL, funcB_num, NULL);

    // 阻塞,资源回收
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);

    // 销毁互斥锁
    // 线程销毁之后, 再去释放互斥锁
    pthread_mutex_destroy(&mutex);

    return 0;
}

3. 死锁

当多个线程访问共享资源,需要加锁,如果锁使用不当,就会造成死锁这种现象。如果线程死锁造成的后果是:所有的线程都被阻塞,并且线程的阻塞是无法解开的(因为可以解锁的线程也被阻塞了)

造成死锁的场景有如下几种:

  1. 加锁之后忘记解锁
// 场景1
void func()
{
    for(int i=0; i<6; ++i)
    {
        // 当前线程A加锁成功, 当前循环完毕没有解锁, 在下一轮循环的时候自己被阻塞了
        // 其余的线程也被阻塞
    	pthread_mutex_lock(&mutex);
    	....
    	.....
        // 忘记解锁
    }
}

// 场景2
void func()
{
    for(int i=0; i<6; ++i)
    {
        // 当前线程A加锁成功
        // 其余的线程被阻塞
    	pthread_mutex_lock(&mutex);
    	....
    	.....
        if(xxx)
        {
            // 函数退出, 没有解锁(解锁函数无法被执行了)
            return ;
        }
        
        pthread_mutex_lock(&mutex);
    }
}
  1. 重复加锁,造成死锁
void func()
{
    for(int i=0; i<6; ++i)
    {
        // 当前线程A加锁成功
        // 其余的线程阻塞
    	pthread_mutex_lock(&mutex);
        // 锁被锁住了, A线程阻塞
        pthread_mutex_lock(&mutex);
    	....
    	.....
        pthread_mutex_unlock(&mutex);
    }
}

// 隐藏的比较深的情况
void funcA()
{
    for(int i=0; i<6; ++i)
    {
        // 当前线程A加锁成功
        // 其余的线程阻塞
    	pthread_mutex_lock(&mutex);
    	....
    	.....
        pthread_mutex_unlock(&mutex);
    }
}

void funcB()
{
    for(int i=0; i<6; ++i)
    {
        // 当前线程A加锁成功
        // 其余的线程阻塞
    	pthread_mutex_lock(&mutex);
        funcA();		// 重复加锁
    	....
    	.....
        pthread_mutex_unlock(&mutex);
    }
}
  1. 在程序中有多个共享资源,因此有很多把锁,随意加锁,导致相互被阻塞
    场景描述:
    (1. )有两个共享资源:X, Y,X对应锁A, Y对应锁B
    • 线程A访问资源X, 加锁A
    • 线程B访问资源Y, 加锁B
      (2. )线程A要访问资源Y, 线程B要访问资源X,因为资源X和Y已经被对应的锁锁住了,因此这个两个线程被阻塞
    • 线程A被锁B阻塞了, 无法打开A锁
    • 线程B被锁A阻塞了, 无法打开B锁

在这里插入图片描述
在使用多线程编程的时候,如何避免死锁呢?
a. 避免多次锁定,多检查
b. 对共享资源访问完毕之后,一定要解锁,或者在加锁的使用 trylock
c. 如果程序中有多把锁,可以控制对锁的访问顺序 (顺序访问共享资源,但在有些情况下是做不到的),另外也可以在对其他互斥锁做加锁操作之前,先释放当前线程拥有的互斥锁。
d. 项目程序中可以引入一些专门用于死锁检测的模块

4. 读写锁

4.1 读写锁函数

读写锁是互斥锁的升级版,在做读操作的时候可以提高程序的执行效率,如果所有的线程都是做读操作, 那么读是并行的,但是使用互斥锁,读操作也是串行的。

读写锁是一把锁,锁的类型为 pthread_rwlock_t,有了类型之后就可以创建一把互斥锁了:

pthread_rwlock_t rwlock;

之所以称其为读写锁,是因为这把锁既可以锁定读操作,也可以锁定写操作。为了方便理解,可以大致认为在这把锁中记录了这些信息:
a. 锁的状态:锁定 / 打开
b. 锁定的是什么操作:读操作 / 写操作,使用读写锁锁定了读操作,需要先解锁才能去锁定写操作,反之亦然。
c. 哪个线程将这把锁锁上了
读写锁的使用方式也互斥锁的使用方式是完全相同的:找共享资源,确定临界区,在临界区的开始位置加锁(读锁 / 写锁),临界区的结束位置解锁。
因为通过一把读写锁可以锁定读或者写操作,下面介绍一下关于读写锁的特点:

  1. 使用读写锁的读锁锁定了临界区,线程对临界区的访问是并行的,读锁是共享的。
  2. 使用读写锁的写锁锁定了临界区,线程对临界区的访问是串行的,写锁是独占的。
  3. 使用读写锁分别对两个临界区加了读锁和写锁,两个线程要同时访问者两个临界区,访问写锁临界区的线程继续运行,访问读锁临界区的线程阻塞,因为写锁比读锁的优先级高。
    如果说程序中所有的线程都对共享资源做写操作,使用读写锁没有优势,和互斥锁是一样的,如果说程序中所有的线程都对共享资源有写也有读操作,并且对共享资源读的操作越多,读写锁更有优势。

Linux 提供的读写锁操作函数原型如下,如果函数调用成功返回 0,失败返回对应的错误号:

#include <pthread.h>
pthread_rwlock_t rwlock;
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
           const pthread_rwlockattr_t *restrict attr);
// 释放读写锁占用的系统资源
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

参数:

  1. rwlock: 读写锁的地址,传出参数
  2. attr: 读写锁属性,一般使用默认属性,指定为 NULL
// 在程序中对读写锁加读锁, 锁定的是读操作
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

调用这个函数,如果读写锁是打开的,那么加锁成功;如果读写锁已经锁定了读操作,调用这个函数依然可以加锁成功,因为读锁是共享的;如果读写锁已经锁定了写操作,调用这个函数的线程会被阻塞。

// 这个函数可以有效的避免死锁
// 如果加读锁失败, 不会阻塞当前线程, 直接返回错误号
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

调用这个函数,如果读写锁是打开的,那么加锁成功;如果读写锁已经锁定了读操作,调用这个函数依然可以加锁成功,因为读锁是共享的;如果读写锁已经锁定了写操作,调用这个函数加锁失败,对应的线程不会被阻塞,可以在程序中对函数返回值进行判断,添加加锁失败之后的处理动作。

// 在程序中对读写锁加写锁, 锁定的是写操作
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

调用这个函数,如果读写锁是打开的,那么加锁成功;如果读写锁已经锁定了读操作或者锁定了写操作,调用这个函数的线程会被阻塞。

// 这个函数可以有效的避免死锁
// 如果加写锁失败, 不会阻塞当前线程, 直接返回错误号
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

调用这个函数,如果读写锁是打开的,那么加锁成功;如果读写锁已经锁定了读操作或者锁定了写操作,调用这个函数加锁失败,但是线程不会阻塞,可以在程序中对函数返回值进行判断,添加加锁失败之后的处理动作。

// 解锁, 不管锁定了读还是写都可用解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

4.2 读写锁使用

题目要求:8 个线程操作同一个全局变量,3 个线程不定时写同一全局资源,5 个线程不定时读同一全局资源。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 全局变量
int number = 0;

// 定义读写锁
pthread_rwlock_t rwlock;

// 写的线程的处理函数
void* writeNum(void* arg)
{
    while(1)
    {
        pthread_rwlock_wrlock(&rwlock);
        int cur = number;
        cur ++;
        number = cur;
        printf("++写操作完毕, number : %d, tid = %ld\n", number, pthread_self());
        pthread_rwlock_unlock(&rwlock);
        // 添加sleep目的是要看到多个线程交替工作
        usleep(rand() % 100);
    }

    return NULL;
}

// 读线程的处理函数
// 多个线程可以如果处理动作相同, 可以使用相同的处理函数
// 每个线程中的栈资源是独享
void* readNum(void* arg)
{
    while(1)
    {
        pthread_rwlock_rdlock(&rwlock);
        printf("--全局变量number = %d, tid = %ld\n", number, pthread_self());
        pthread_rwlock_unlock(&rwlock);
        usleep(rand() % 100);
    }
    return NULL;
}

int main()
{
    // 初始化读写锁
    pthread_rwlock_init(&rwlock, NULL);

    // 3个写线程, 5个读的线程
    pthread_t wtid[3];
    pthread_t rtid[5];
    for(int i=0; i<3; ++i)
    {
        pthread_create(&wtid[i], NULL, writeNum, NULL);
    }

    for(int i=0; i<5; ++i)
    {
        pthread_create(&rtid[i], NULL, readNum, NULL);
    }

    // 释放资源
    for(int i=0; i<3; ++i)
    {
        pthread_join(wtid[i], NULL);
    }

    for(int i=0; i<5; ++i)
    {
        pthread_join(rtid[i], NULL);
    }

    // 销毁读写锁
    pthread_rwlock_destroy(&rwlock);

    return 0;
}

可观察到下列运行的结果:

Thread read, id = 140045742888704, number = 0
Thread read, id = 140045734496000, number = 0
Thread read, id = 140045717710592, number = 0
Thread read, id = 140045709317888, number = 0
Thread B, id = 140045700925184, number = 1
Thread read, id = 140045742888704, number = 1
Thread read, id = 140045717710592, number = 1
Thread B, id = 140045692532480, number = 2
Thread read, id = 140045709317888, number = 2
Thread B, id = 140045684139776, number = 3
Thread read, id = 140045742888704, number = 3
Thread read, id = 140045717710592, number = 3
Thread read, id = 140045709317888, number = 3
Thread read, id = 140045717710592, number = 3
Thread read, id = 140045742888704, number = 3
Thread read, id = 140045709317888, number = 3
Thread B, id = 140045684139776, number = 4
Thread read, id = 140045734496000, number = 4
Thread B, id = 140045700925184, number = 5
Thread B, id = 140045692532480, number = 6
Thread read, id = 140045726103296, number = 6
Thread B, id = 140045700925184, number = 7
Thread read, id = 140045734496000, number = 7
Thread B, id = 140045692532480, number = 8
Thread read, id = 140045742888704, number = 8
Thread read, id = 140045726103296, number = 8
Thread read, id = 140045709317888, number = 8
Thread read, id = 140045717710592, number = 8
Thread B, id = 140045684139776, number = 9
Thread read, id = 140045734496000, number = 9
Thread B, id = 140045700925184, number = 10
Thread read, id = 140045742888704, number = 10
Thread read, id = 140045709317888, number = 10
Thread read, id = 140045717710592, number = 10
Thread B, id = 140045684139776, number = 11
Thread read, id = 140045734496000, number = 11
Thread B, id = 140045692532480, number = 12
Thread B, id = 140045700925184, number = 13
Thread B, id = 140045684139776, number = 14
Thread read, id = 140045717710592, number = 14
Thread read, id = 140045734496000, number = 14
Thread B, id = 140045692532480, number = 15
Thread read, id = 140045726103296, number = 15
Thread B, id = 140045684139776, number = 16
Thread read, id = 140045717710592, number = 16
Thread read, id = 140045734496000, number = 16
Thread B, id = 140045700925184, number = 17
Thread B, id = 140045692532480, number = 18
Thread read, id = 140045726103296, number = 18
Thread B, id = 140045684139776, number = 19
Thread B, id = 140045684139776, number = 20
Thread read, id = 140045726103296, number = 20
Thread B, id = 140045684139776, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045709317888, number = 21
Thread read, id = 140045717710592, number = 21
Thread read, id = 140045742888704, number = 21
Thread read, id = 140045709317888, number = 21
Thread B, id = 140045684139776, number = 22
Thread read, id = 140045726103296, number = 22
Thread read, id = 140045742888704, number = 22
Thread read, id = 140045717710592, number = 22
Thread read, id = 140045709317888, number = 22
Thread read, id = 140045717710592, number = 22
Thread B, id = 140045684139776, number = 23
Thread read, id = 140045717710592, number = 23
Thread read, id = 140045726103296, number = 23
Thread B, id = 140045684139776, number = 24
Thread read, id = 140045742888704, number = 24
Thread read, id = 140045709317888, number = 24
Thread read, id = 140045742888704, number = 24
Thread read, id = 140045709317888, number = 24
Thread read, id = 140045709317888, number = 24
Thread read, id = 140045742888704, number = 24
Thread read, id = 140045742888704, number = 24
Thread read, id = 140045709317888, number = 24
Thread read, id = 140045709317888, number = 24
Thread read, id = 140045742888704, number = 24
Thread B, id = 140045684139776, number = 25
Thread read, id = 140045709317888, number = 25
Thread read, id = 140045742888704, number = 25
Thread B, id = 140045684139776, number = 26
Thread read, id = 140045742888704, number = 26
Thread B, id = 140045684139776, number = 27
Thread read, id = 140045709317888, number = 27
Thread read, id = 140045742888704, number = 27
Thread B, id = 140045684139776, number = 28
Thread read, id = 140045742888704, number = 28
Thread B, id = 140045684139776, number = 29
Thread read, id = 140045717710592, number = 29
Thread read, id = 140045726103296, number = 29
Thread read, id = 140045742888704, number = 29
Thread B, id = 140045684139776, number = 30
Thread read, id = 140045709317888, number = 30
Thread B, id = 140045684139776, number = 31
Thread read, id = 140045709317888, number = 31
Thread read, id = 140045742888704, number = 31
Thread B, id = 140045684139776, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045726103296, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045726103296, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045726103296, number = 32
Thread read, id = 140045726103296, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045734496000, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045734496000, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045734496000, number = 32
Thread read, id = 140045717710592, number = 32
Thread read, id = 140045742888704, number = 32
Thread read, id = 140045709317888, number = 32
Thread B, id = 140045684139776, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045726103296, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045734496000, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045742888704, number = 33
Thread read, id = 140045709317888, number = 33
Thread read, id = 140045717710592, number = 33
Thread read, id = 140045734496000, number = 33
Thread B, id = 140045700925184, number = 34
Thread read, id = 140045709317888, number = 34
Thread read, id = 140045717710592, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045734496000, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045709317888, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045717710592, number = 34
Thread read, id = 140045734496000, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045709317888, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045709317888, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045734496000, number = 34
Thread read, id = 140045717710592, number = 34
Thread read, id = 140045742888704, number = 34
Thread read, id = 140045726103296, number = 34
Thread B, id = 140045692532480, number = 35
Thread B, id = 140045684139776, number = 36
Thread B, id = 140045700925184, number = 37
Thread read, id = 140045709317888, number = 37
Thread B, id = 140045684139776, number = 38
Thread B, id = 140045692532480, number = 39
Thread read, id = 140045709317888, number = 39
Thread read, id = 140045726103296, number = 39
Thread B, id = 140045700925184, number = 40
Thread B, id = 140045684139776, number = 41
Thread read, id = 140045726103296, number = 41
Thread B, id = 140045692532480, number = 42
Thread read, id = 140045709317888, number = 42
Thread B, id = 140045700925184, number = 43
Thread B, id = 140045684139776, number = 44
Thread read, id = 140045726103296, number = 44
Thread read, id = 140045709317888, number = 44
Thread read, id = 140045717710592, number = 44
Thread read, id = 140045734496000, number = 44
Thread B, id = 140045692532480, number = 45
Thread read, id = 140045726103296, number = 45
Thread read, id = 140045717710592, number = 45
Thread read, id = 140045734496000, number = 45
Thread read, id = 140045726103296, number = 45
Thread read, id = 140045734496000, number = 45
Thread read, id = 140045717710592, number = 45
Thread read, id = 140045734496000, number = 45
Thread read, id = 140045717710592, number = 45
Thread read, id = 140045726103296, number = 45
Thread read, id = 140045734496000, number = 45
Thread read, id = 140045717710592, number = 45
Thread read, id = 140045726103296, number = 45
Thread read, id = 140045734496000, number = 45
Thread read, id = 140045717710592, number = 45
Thread read, id = 140045742888704, number = 45
Thread read, id = 140045742888704, number = 45
Thread read, id = 140045742888704, number = 45
Thread read, id = 140045726103296, number = 45
Thread read, id = 140045709317888, number = 45
Thread B, id = 140045684139776, number = 46
Thread read, id = 140045742888704, number = 46
Thread read, id = 140045726103296, number = 46
Thread read, id = 140045742888704, number = 46
Thread read, id = 140045726103296, number = 46
Thread read, id = 140045742888704, number = 46
Thread read, id = 140045726103296, number = 46
Thread read, id = 140045734496000, number = 46
Thread read, id = 140045717710592, number = 46
Thread B, id = 140045692532480, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045709317888, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045742888704, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045717710592, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045717710592, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045742888704, number = 47
Thread read, id = 140045717710592, number = 47
Thread read, id = 140045726103296, number = 47
Thread read, id = 140045742888704, number = 47
Thread read, id = 140045717710592, number = 47
Thread read, id = 140045734496000, number = 47
Thread read, id = 140045742888704, number = 47
Thread read, id = 140045734496000, number = 47
Thread read, id = 140045742888704, number = 47
Thread read, id = 140045742888704, number = 47
Thread read, id = 140045734496000, number = 47
Thread read, id = 140045726103296, number = 47
Thread B, id = 140045684139776, number = 48
Thread B, id = 140045700925184, number = 49
Thread read, id = 140045709317888, number = 49
Thread B, id = 140045692532480, number = 50
Thread B, id = 140045684139776, number = 51
Thread read, id = 140045726103296, number = 51
Thread read, id = 140045742888704, number = 51
Thread B, id = 140045692532480, number = 52
Thread B, id = 140045684139776, number = 53
Thread read, id = 140045726103296, number = 53
Thread read, id = 140045734496000, number = 53
Thread B, id = 140045692532480, number = 54
Thread B, id = 140045684139776, number = 55
Thread read, id = 140045742888704, number = 55
Thread read, id = 140045726103296, number = 55
Thread B, id = 140045692532480, number = 56
Thread B, id = 140045684139776, number = 57
Thread read, id = 140045726103296, number = 57
Thread read, id = 140045734496000, number = 57
Thread B, id = 140045692532480, number = 58
Thread read, id = 140045726103296, number = 58
Thread read, id = 140045726103296, number = 58
Thread read, id = 140045709317888, number = 58
Thread read, id = 140045726103296, number = 58
Thread read, id = 140045709317888, number = 58
Thread read, id = 140045734496000, number = 58
Thread B, id = 140045684139776, number = 59
Thread read, id = 140045709317888, number = 59
Thread read, id = 140045734496000, number = 59
Thread B, id = 140045700925184, number = 60
Thread B, id = 140045692532480, number = 61
Thread B, id = 140045684139776, number = 62
Thread read, id = 140045734496000, number = 62
Thread B, id = 140045700925184, number = 63
Thread B, id = 140045692532480, number = 64
Thread B, id = 140045684139776, number = 65
Thread read, id = 140045734496000, number = 65
Thread B, id = 140045700925184, number = 66
Thread B, id = 140045692532480, number = 67
Thread B, id = 140045684139776, number = 68
Thread read, id = 140045734496000, number = 68
Thread B, id = 140045700925184, number = 69
Thread B, id = 140045692532480, number = 70
Thread B, id = 140045684139776, number = 71
Thread read, id = 140045734496000, number = 71
Thread B, id = 140045700925184, number = 72
Thread B, id = 140045692532480, number = 73
Thread B, id = 140045684139776, number = 74
Thread read, id = 140045734496000, number = 74
Thread B, id = 140045700925184, number = 75
Thread B, id = 140045692532480, number = 76
Thread B, id = 140045684139776, number = 77
Thread read, id = 140045734496000, number = 77
Thread B, id = 140045700925184, number = 78
Thread read, id = 140045734496000, number = 78
Thread B, id = 140045692532480, number = 79
Thread B, id = 140045684139776, number = 80
Thread B, id = 140045700925184, number = 81
Thread read, id = 140045734496000, number = 81
Thread B, id = 140045692532480, number = 82
Thread B, id = 140045684139776, number = 83
Thread B, id = 140045700925184, number = 84
Thread read, id = 140045734496000, number = 84
Thread B, id = 140045692532480, number = 85
Thread B, id = 140045684139776, number = 86
Thread read, id = 140045734496000, number = 86
Thread B, id = 140045700925184, number = 87
Thread B, id = 140045692532480, number = 88
Thread B, id = 140045700925184, number = 89
Thread read, id = 140045734496000, number = 89
Thread B, id = 140045692532480, number = 90
Thread B, id = 140045684139776, number = 91
Thread B, id = 140045700925184, number = 92
Thread read, id = 140045734496000, number = 92
Thread B, id = 140045692532480, number = 93
Thread B, id = 140045684139776, number = 94
Thread B, id = 140045700925184, number = 95
Thread read, id = 140045726103296, number = 95
Thread B, id = 140045692532480, number = 96
Thread B, id = 140045684139776, number = 97
Thread B, id = 140045700925184, number = 98
Thread B, id = 140045692532480, number = 99
Thread B, id = 140045684139776, number = 100
Thread B, id = 140045700925184, number = 101
Thread B, id = 140045692532480, number = 102
Thread B, id = 140045684139776, number = 103
Thread B, id = 140045700925184, number = 104
Thread B, id = 140045692532480, number = 105
Thread B, id = 140045684139776, number = 106
Thread B, id = 140045700925184, number = 107
Thread B, id = 140045692532480, number = 108
Thread B, id = 140045684139776, number = 109
Thread B, id = 140045700925184, number = 110
Thread B, id = 140045692532480, number = 111
Thread B, id = 140045684139776, number = 112
Thread B, id = 140045700925184, number = 113
Thread B, id = 140045692532480, number = 114
Thread B, id = 140045684139776, number = 115
Thread B, id = 140045700925184, number = 116
Thread B, id = 140045692532480, number = 117
Thread B, id = 140045700925184, number = 118
Thread B, id = 140045692532480, number = 119
Thread B, id = 140045700925184, number = 120
Thread B, id = 140045692532480, number = 121
Thread B, id = 140045700925184, number = 122
Thread B, id = 140045692532480, number = 123
Thread B, id = 140045700925184, number = 124
Thread B, id = 140045692532480, number = 125
Thread B, id = 140045700925184, number = 126
Thread B, id = 140045692532480, number = 127
Thread B, id = 140045700925184, number = 128
Thread B, id = 140045692532480, number = 129
Thread B, id = 140045700925184, number = 130
Thread B, id = 140045692532480, number = 131
Thread B, id = 140045700925184, number = 132
Thread B, id = 140045692532480, number = 133
Thread B, id = 140045700925184, number = 134
Thread B, id = 140045692532480, number = 135
Thread B, id = 140045700925184, number = 136
Thread B, id = 140045692532480, number = 137
Thread read, id = 140045726103296, number = 137
Thread B, id = 140045700925184, number = 138
Thread B, id = 140045692532480, number = 139
Thread B, id = 140045700925184, number = 140
Thread B, id = 140045692532480, number = 141
Thread read, id = 140045726103296, number = 141
Thread B, id = 140045700925184, number = 142
Thread B, id = 140045692532480, number = 143
Thread read, id = 140045726103296, number = 143
Thread B, id = 140045700925184, number = 144
Thread B, id = 140045692532480, number = 145
Thread read, id = 140045726103296, number = 145
Thread B, id = 140045700925184, number = 146
Thread read, id = 140045726103296, number = 146
Thread read, id = 140045726103296, number = 146
Thread B, id = 140045700925184, number = 147
Thread read, id = 140045726103296, number = 147
Thread B, id = 140045700925184, number = 148
Thread B, id = 140045700925184, number = 149
Thread B, id = 140045700925184, number = 150

作者: 苏丙榅
链接: https://subingwen.cn/linux/thread-sync/#5-1-%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F%E5%87%BD%E6%95%B0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值