• 【Linux 线程】线程同步《一》


    1、线程同步概念

    线程同步:在多个线程访问共享数据时,有先后次序。

    在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。
    线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,目前实现线程同步的方法有很多,临界区对象就是其中一种。
    常见的多线程同步的方式:临界区、互斥量、事件、信号量。
    临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)的区别如下:
    1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
    2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
    3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
    4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。
     

    2、互斥锁

    posix下抽象了一个锁类型的结构:ptread_mutex_t。通过对该结构的操作,来判断资源是否可以访问。顾名思义,加锁(lock)后,别人就无法打开,只有当锁没有关闭(unlock)的时候才能访问资源。

    主要有如下5个函数:

    1)pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex attr_t *attr);
    // 初始化锁变量mutex。
    // attr为锁属性(自己的值只能被自己的指针改变),NULL值为默认属性。
    2)pthread_mutex_lock(pthread_mutex_t *mutex);
    // 加锁(阻塞操作)
    3)pthread_mutex_trylock(pthread_mutex_t *mutex);
    // 试图加锁(不阻塞操作)
    // 当互斥锁空闲时将占有该锁;否则立即返回
    // 但是与2不一样的是当锁已经在使用的时候,返回为EBUSY,而不是挂起等待。
    4)pthread_mutex_unlock(pthread_mutex_t *mutex);
    // 释放锁
    
    (5)pthread_mutex_destroy(pthread_mutex_t *mutex);
    // 使用完后删除

    举例1:

      1 /*************************************************************************
      2     > File Name: pthread_mutex1.c
      3     > Summary: 互斥锁举例1
      4     > Author: xuelisheng 
      5     > Created Time: 2018年12月17日
      6  ************************************************************************/
      7 /*
      8 #include <stdio.h>
      9 #include <stdlib.h>  
     10 #include <unistd.h>  
     11 #include <pthread.h>  
     12 typedef struct ct_sum  
     13 {   
     14     int sum;  
     15     pthread_mutex_t lock;  
     16 }ct_sum;  
     17 
     18 void * add1(void * cnt)  
     19 {       
     20     // 加互斥锁
     21     pthread_mutex_lock(&(((ct_sum*)cnt)->lock));  
     22     int i;  
     23     for(i=0; i<50; i++)
     24     {  
     25         (*(ct_sum*)cnt).sum+=i;
     26     } 
     27     printf("thread 1 printf sum = %d
    ", ((ct_sum *)cnt)->sum);
     28     pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));  
     29     pthread_exit(NULL);  
     30     return 0;  
     31 }  
     32 
     33 void * add2(void *cnt)  
     34 {       
     35     int i;  
     36     cnt= (ct_sum*)cnt;  
     37     pthread_mutex_lock(&(((ct_sum*)cnt)->lock));  
     38     for(i=50; i<101; i++)  
     39     {    
     40         (*(ct_sum*)cnt).sum+=i;         
     41     }  
     42     printf("thread 2 printf sum = %d
    ", ((ct_sum *)cnt)->sum);
     43     pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));  
     44     pthread_exit(NULL);  
     45     return 0;  
     46 }  
     47 
     48 int main(void)  
     49 {   
     50     int i;  
     51     pthread_t ptid1, ptid2;  
     52     int sum = 0;  
     53     ct_sum cnt;  
     54     //初始化互斥锁
     55     pthread_mutex_init(&(cnt.lock),NULL);  
     56     cnt.sum=0;  
     57     // 创建线程1
     58     pthread_create(&ptid1,NULL,add1,&cnt);  
     59     // 创建线程2
     60     pthread_create(&ptid2,NULL,add2,&cnt);  
     61 
     62     pthread_mutex_lock(&(cnt.lock));  
     63     printf("sum %d
    ",cnt.sum);  
     64     pthread_mutex_unlock(&(cnt.lock));  
     65     pthread_join(ptid1,NULL);  
     66     pthread_join(ptid2,NULL);  
     67     pthread_mutex_destroy(&(cnt.lock));  
     68     return 0;  
     69 }
     70 */
     71 
     72 /*
     73 关于pthread_exit和pthread_join的联合使用
     74 一般都是pthread_exit在线程内退出,然后返回一个值。这个时候就跳到主线程的pthread_join了(因为一直在等你结束),这个返回值会直接送到pthread_join,实现了主与分线程的通信。
     75 */
     76 #include <stdio.h>
     77 #include <stdlib.h>  
     78 #include <unistd.h>  
     79 #include <pthread.h>  
     80 typedef struct ct_sum1 
     81 {   
     82     int sum;  
     83     pthread_mutex_t lock;  
     84 };  
     85 
     86 struct ct_sum1 ct_sum;
     87 
     88 void * add1()  
     89 {       
     90     // 加互斥锁
     91     pthread_mutex_lock(&ct_sum.lock);  
     92     int i;  
     93     for(i=0; i<50; i++)
     94     {  
     95         ct_sum.sum+=i;
     96     } 
     97     printf("thread 1 printf sum = %d
    ", ct_sum.sum);
     98     // 解锁
     99     pthread_mutex_unlock(&ct_sum.lock);  
    100     // pthread_exit用于强制退出一个线程(非执行完毕退出),一般用于线程内部。
    101     pthread_exit(NULL);  
    102     return 0;  
    103 }  
    104 
    105 void * add2()  
    106 {       
    107     int i;   
    108      // 加互斥锁
    109     pthread_mutex_lock(&ct_sum.lock);  
    110     for(i=50; i<101; i++)  
    111     {    
    112         ct_sum.sum+=i;         
    113     }  
    114     printf("thread 2 printf sum = %d
    ", ct_sum.sum);
    115     // 解锁
    116     pthread_mutex_unlock(&ct_sum.lock);  
    117     // pthread_exit用于强制退出一个线程(非执行完毕退出),一般用于线程内部。
    118     pthread_exit(NULL);  
    119     return 0;  
    120 }  
    121 
    122 int main(void)  
    123 {   
    124     ct_sum.sum = 0;
    125     pthread_t ptid1, ptid2;  
    126     int sum = 0;  
    127     //初始化互斥锁
    128     pthread_mutex_init(&(ct_sum.lock),NULL);  
    129     // 创建线程1
    130     pthread_create(&ptid1,NULL,add1,NULL);  
    131     // 创建线程2
    132     pthread_create(&ptid2,NULL,add2,NULL);  
    133     // 阻塞主线程,回收子线程
    134     pthread_join(ptid1,NULL);  
    135     pthread_join(ptid2,NULL);  
    136      
    137     printf("sum %d
    ",ct_sum.sum);  
    138     // 销毁互斥锁
    139     pthread_mutex_destroy(&(ct_sum.lock));  
    140     return 0;  
    141 }

    运行结果:

    thread 1 printf sum = 1225
    thread 2 printf sum = 5050
    sum 5050

    举例2:

     1 /*************************************************************************
     2     > File Name: pthread_mutex2.c
     3     > Summary: 互斥锁举例2(多线程写文件---没有加锁的情形)
     4     > Author: xuelisheng 
     5     > Created Time: 2018年12月17日
     6  ************************************************************************/
     7 #include <stdio.h> 
     8 #include <pthread.h> 
     9 #include <malloc.h> 
    10 
    11 const char filename[] = "hello"; 
    12 void* thread(void *id)
    13 { 
    14     int num = *(int *)id; // 写文件的操作 
    15     FILE *fp = fopen(filename, "a+"); 
    16     int start = *((int *)id); 
    17     int end = start + 1; 
    18     setbuf(fp, NULL);// 设置缓冲区的大小 
    19     fprintf(stdout, "%d
    ", start); 
    20     for (int i = (start * 10); i < (end * 10); i ++)
    21     { 
    22         // 写文件
    23         fprintf(fp, "%d	", i); 
    24     } 
    25     fprintf(fp, "
    "); 
    26     fclose(fp); 
    27     return NULL; 
    28 }
    29 
    30 int main()
    31 { 
    32     // 设置线程个数
    33     int num_thread = 5;
    34     // 堆上申请指向每个线程的指针 
    35     pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread); 
    36     int * id = (int *)malloc(sizeof(int) * num_thread); 
    37     for (int i = 0; i < num_thread; i++)
    38     { 
    39         id[i] = i; 
    40         if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0)
    41         { 
    42             printf("thread create failed!
    "); 
    43             return 1; 
    44         } 
    45     } 
    46     for (int i = 0; i < num_thread; i++)
    47     { 
    48         pthread_join(pt[i], NULL); 
    49     } // 释放资源 
    50     free(pt); 
    51     free(id); 
    52     return 0; 
    53 }

    运行结果:

    屏幕输出:

    1
    0
    2
    3
    4

    输出文件(hello):

    举例3(更改举例2出现的问题):

     1 /*************************************************************************
     2     > File Name: pthread_mutex3.c
     3     > Summary: 互斥锁举例3(多线程写文件---加锁的情形)
     4     > Author: xuelisheng 
     5     > Created Time: 2018年12月17日
     6  ************************************************************************/
     7 #include <stdio.h> 
     8 #include <pthread.h> 
     9 #include <malloc.h> 
    10 
    11 pthread_mutex_t mutex;
    12 const char filename[] = "hello"; 
    13 void* thread(void *id)
    14 { 
    15     int num = *(int *)id; // 写文件的操作
    16     // 加锁
    17     if (pthread_mutex_lock(&mutex) != 0){
    18                 fprintf(stdout, "lock error!
    ");
    19     } 
    20     FILE *fp = fopen(filename, "a+"); 
    21     int start = *((int *)id); 
    22     int end = start + 1; 
    23     setbuf(fp, NULL);// 设置缓冲区的大小 
    24     fprintf(stdout, "%d
    ", start); 
    25     for (int i = (start * 10); i < (end * 10); i ++)
    26     { 
    27         // 写文件
    28         fprintf(fp, "%d	", i); 
    29     } 
    30     fprintf(fp, "
    "); 
    31     fclose(fp); 
    32     // 解锁
    33     pthread_mutex_unlock(&mutex);
    34     return NULL; 
    35 }
    36 
    37 int main()
    38 { 
    39     // 设置线程个数
    40     int num_thread = 5;
    41     // 堆上申请指向每个线程的指针 
    42     pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread); 
    43     int * id = (int *)malloc(sizeof(int) * num_thread); 
    44     // 初始化互斥锁 
    45     if (pthread_mutex_init(&mutex, NULL) != 0)
    46     { 
    47         // 互斥锁初始化失败 
    48         free(pt); 
    49         free(id); 
    50         return 1; 
    51     }
    52 
    53     for (int i = 0; i < num_thread; i++)
    54     { 
    55         id[i] = i; 
    56         if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0)
    57         { 
    58             printf("thread create failed!
    "); 
    59             return 1; 
    60         } 
    61     } 
    62     for (int i = 0; i < num_thread; i++)
    63     { 
    64         pthread_join(pt[i], NULL); 
    65     } // 释放资源 
    66 
    67     pthread_mutex_destroy(&mutex);
    68     free(pt); 
    69     free(id); 
    70     return 0; 
    71 }

    运行结果:

    屏幕输出:

    0
    1
    2
    3
    4

    输出文件(hello):

  • 相关阅读:
    SWTDesigner注册器
    C# 创建、部署和调用WebService的简单示例
    (android实战)应用在线版本更新
    jQuery获取Select选择的Text和 Value(转)
    Android 判断sd卡和sim卡是否可用
    Android开发中如何固定屏幕显示!
    入侵网站简单方法总结
    【Android】防止UI界面被输入法遮挡(画面随输入法自适应)
    关于字符编码的问题
    最好用的mysql密码忘记的解决方法
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/10132333.html
Copyright © 2020-2023  润新知