多线程共享一个进程的地址空间虽然线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制。

1.线程间的互斥,引入互斥锁的目的是用来保证共享资源数据操作的完整性。
互斥锁主要用来保护临界资源,每个邻界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源。
线程必须先获得互斥锁才能访问临界资源,访问完临界资源后释放该锁。如果无法获得锁,线程会阻塞知道获得锁为止。

2同步指的是多个任务按照约定的顺序相互配合完成一件事情,

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 int g_count=0;
  4 void* pthread_run(void* arg)
  5 {
  6     int count=5000;
  7     int tmp;
  8     while(count--)
  9     {
 10         tmp=g_count;
 11         printf("pthread_id:%d ,g_count: %d\n",(int)arg,tmp);
 12         g_count=tmp+1;
 13     }
 14     return (void*)0;
 15 }
 16 int main()
 17 {
 18     pthread_t tid1,tid2;
 19     pthread_create(&tid1,NULL,pthread_run,(void*)1);
 20     pthread_create(&tid2,NULL,pthread_run,(void*)2);
 21     sleep(1);
 22     printf("finally,g_count:  %d\n",g_count);
 23     return 0;
 24 }

按照预期结果,应该打印出10000,把g_count加10000次

但是,

wKiom1cTLdjw5oRRAABbVvBmdj4738.png

这是因为两个线程同时对全局变量进行操作时产生干扰。

多线程的互斥操作主要用互斥量来实现,保护临界资源,对临界区加锁。

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

int pthread_mutex_init(pthread_mutex_t *restrict mutex,

const pthread_mutexattr_t *restrict attr);//attr一般使用默认值为NULL

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

加锁,去锁函数

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);//阻塞式

int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞式

int pthread_mutex_unlock(pthread_mutex_t *mutex);

实现加锁

  1 #include<stdio.h>
  2 #include<pthread.h>
  3 pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
  4 int g_count=0;
  5 void* pthread_run(void* arg)
  6 {
  7     int count=5000;
  8     int tmp;
  9     while(count--)
 10     {
 11         pthread_mutex_lock(&mutex);
 12         tmp=g_count;
 13         printf("pthread_id:%d ,g_count: %d\n",(int)arg,tmp);
 14         g_count=tmp+1;
 15         pthread_mutex_unlock(&mutex);
 16     }
 17     return (void*)0;
 18 }
 19 int main()
 20 {
 21     pthread_t tid1,tid2;
 22     pthread_create(&tid1,NULL,pthread_run,(void*)1);
 23     pthread_create(&tid2,NULL,pthread_run,(void*)2);
 24     sleep(1);
 25     pthread_mutex_destroy(&mutex);
 26     printf("finally,g_count:  %d\n",g_count);
 27     return 0;
 28 }

wKioL1cTMfewUU6hAABKGM2-OOg169.png

总结:加锁时应该考虑问题的规模选择加锁的粒度,应从安全,性能,稳定性三方面来考虑。

加锁时,粒度越大,申请锁的次数少,申请锁时会占代码资源;

加锁加的越早,并行性能降低,程序执行效率也随之降低。