【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):

原文地址:https://www.cnblogs.com/xuelisheng/p/10132333.html