C++线程同步实战

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chengqiuming/article/details/89278191

一 点睛

线程同步基本思想:同步各个线程对资源(比如全局变量、文件)的访问。如果不对资源访问进行线程同步,则会产生资源访问冲突的问题。对于多线程程序,访问冲突问题是很普遍的问题,解决方法是引入锁(互斥锁、读写锁),获得锁的线程可以完成“读——修改——写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能访问共享数据,这样“读——修改——写”3步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其他处理器上并行做这个操作。

二 举例

一个线程正在读取一个全局变量,虽然读取全局变量的这个语句在C/C++源代码中是一条语句,但编译为机器代码后,CPU指令处理这个过程的时候,需要多条指令来处理这个读取变量的过程,如果这一系列的指令被其他一个线程打断了,也就是说CPU还没执行完全部读取变量的所有指令,而去执行另外一个线程了,另外一个线程对这个全局变量进行修改,这样修改完后又返回原来的线程,继续执行读取变量的指令,此时变量的值已经改变了,这样第一个线程的执行结果就不是预期结果。

三 多线程访问共享变量造成竞争例子

1 步骤说明

从内存单元读入寄存器

把寄存器中的值增加

把寄存器中的新值写回内存单元

2 图例

当两个线程对同一个变量做增操作时候,会出现下面情况:

 如果两个线程在串行操作下分别对i进行累加,那么i的值应该是7,但上图两个线程并行执行结果是是6.因为B线程并没有等A线程做完i+1后开始执行,而是A线程刚刚把i从内存读入寄存器后就开始执行了,所以B线程也是在i=5的时候开始执行,这样A执行结果是6,B执行的结果也是6.因此在这种没有做同步的情况下,多个线程对全局变量的进行累加,最终结果是小于或者等于它们串行操作的结果。

3 代码

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <string.h>
#include <cstdlib>
int gcn = 0;   //定义一个全局变量,用于累加

void *thread_1(void *arg) {    //第一个线程
    int j;
    for (j = 0; j < 100000000; j++) {  //开始累加
        gcn++;
    }  
    pthread_exit((void *)0);
}

void *thread_2(void *arg) {   //第二个线程
    int j;
    for (j = 0; j < 100000000; j++) {   //开始累加     
        gcn++;
    }  
    pthread_exit((void *)0);
}
int main(void)
{
    int j,err;
    pthread_t th1, th2;
     
    for (j = 0; j < 10; j++)   //做10次测试
    {
        err = pthread_create(&th1, NULL, thread_1, (void *)0);   //创建第1个线程
        if (err != 0) {
            printf("create new thread error:%s\n", strerror(err));
            exit(0);
        }  
        err = pthread_create(&th2, NULL, thread_2, (void *)0);   //创建第2个线程
        if (err != 0) {
            printf("create new thread error:%s\n", strerror(err));
            exit(0);
        }  
           
        err = pthread_join(th1, NULL);   //等待第一个线程结束
        if (err != 0) {
            printf("wait thread done error:%s\n", strerror(err));
            exit(1);
        }
        err = pthread_join(th2, NULL);   //等待第二个线程结束
        if (err != 0) {
            printf("wait thread done error:%s\n", strerror(err));
            exit(1);
        }
        printf("gcn=%d\n", gcn);
        gcn = 0;
    }
    return 0;
}

4 执行结果

[root@localhost test]# g++ -o test test.cpp -lpthread
[root@localhost test]# ./test
gcn=200000000
gcn=200000000
gcn=200000000
gcn=200000000
gcn=194321031
gcn=200000000
gcn=200000000
gcn=193992148
gcn=200000000
gcn=194882538

5 说明

从结果看到,有几次没有达到200000000,是因为累加操作编译成机器代码后,变成3个操作,3个操作无法组成原子操作,被另外一个线程打断。

猜你喜欢

转载自blog.csdn.net/chengqiuming/article/details/89278191