拥有梦想是一种智力,实现梦想是一种能力。

 

概述

线程共享同一进程的地址空间,线程间通过全局变量交换数据进行通信。为了避免多个进程同时访问同一个共享资源而引发冲突,多个线程访问共享数据时需要同步或互斥机制

同步机制:指的是多个任务按照约定的先后次序相互配合完成一件事情

互斥机制:一次只允许一个任务(进程、线程)访问的共享资源

 

同步机制

信号量:代表某一类资源,其值表示系统中该资源的数量,由信号量来决定线程是继续运行还是阻塞等待。分为无名信号量(基于内存的信号量,本章讨论)和有名信号量。

就比如,我这里是网吧,那现在来了一个人开了一个机子,申请了一个资源,我这里可用电脑数量就会少一,如果有个人走了,关了一个自己,释放了一个资源,我这里可使用的电脑数量就会多一,电脑就可以理解成这个资源,这个初始值是多少呢,这个我们来定,根据需要写就可以

 

信号量是一个受保护的变量,只能通过三种操作来访问

  • 初始化
  • P操作(申请资源)
  • V操作(释放资源)

 

要使用同步机制管理线程,线使用sem_init初始化信号量sem,指定资源的数量。完成任务后使用sem_post,如果要用资源使用sem_post,V操作作用是释放资源,增加空余资源 sem++ ; P操作作用是申请资源,如果sem是0,它就申请不到资源的,就不会执行申请,会等释放后才能申请,申请成功后减少空余资源sem--。

 

初始化信号量

 #include  <semaphore.h>
 int  sem_init(sem_t *sem, int pshared, unsigned int val);

  •  成功时返回0,失败时EOF
  •  sem  指向要初始化的信号量对象
  •  pshared   0 – 线程间   1 – 进程间
  •  val  信号量初值

 

释放资源(V)/申请资源(P)

 #include  <semaphore.h>
 int  sem_wait(sem_t  *sem);       P操作
 int  sem_post(sem_t  *sem);       V操作

  •  成功时返回0,失败时返回EOF
  •  sem  指向要操作的信号量对象

 

示例

函数的功能是,主线程读取我们从终端写入的数据到buf,子线程sem_wait是等待主线程释放资源后进行相关操作。
 

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

char buf[32];
sem_t  sem;
void *function(void *arg);
int main(void) {
  pthread_t  a_thread;

  if (sem_init(&sem, 0,  0) < 0) {
	perror(“sem_init”);  exit(-1);
  }
  if (pthread_create(&a_thread, NULL, function, NULL) != 0) {
	printf(“fail to pthread_create”);  
	exit(-1); 
  }
  printf(“input ‘quit’ to exit\n”);
  do {
    fgets(buf, 32, stdin);
    sem_post(&sem); 
  }  while (strncmp(buf, “quit”, 4) != 0);
 
  return 0;
}

void  *function(void *arg) {
   while ( 1 ) {
	  sem_wait(&sem);
	  printf(“you enter %d characters\n”, strlen(buf));
   } 
}

V操作表示已经把这个资源利用过了,释放一个资源;P操作是即将用到一个资源,申请一个资源。
比如一个人关了机子离开网吧,sem(信号量)控制另一个人来开机子。

sem表示资源的数量,释放是增加空余资源 sem++ ; 申请是减少空余资源sem--
这里sem的变化是 开始0 -> 主线程1 -> 子线程0 ->......

 

gcc 编译的时候注意加载库 -pthread

 

 

 

 

互斥机制

 

临界资源: 一次只允许一个任务(进程、线程)访问的共享资源

临界区:访问临界资源的代码

互斥机制:mutex互斥锁,任务访问临界资源前申请锁,访问完后释放锁

 

互斥锁的三种操作

  • 初始化互斥锁
  • 申请锁
  • 释放锁

 

因为,互斥机制一次只允许一个任务(进程、线程)访问的共享资源。通过互斥锁构造这种机制,首先要pthread_mutex_init初始化互斥锁,然后我们在访问临界资源的代码之前用pthread_mutex_lock来给临界区·加锁·,结束再pthread_mutex_unlock释放锁。

 

初始化互斥锁

 #include  <pthread.h>
 int  pthread_mutex_init(pthread_mutex_t *mutex,
       const pthread_mutexattr_t *  attr);

  •  成功时返回0,失败时返回错误码
  •  mutex  指向要初始化的互斥锁对象
  •  attr  互斥锁属性,NULL表示缺省属性

 

申请锁

 #include  <pthread.h>
 int  pthread_mutex_lock(pthread_mutex_t *mutex);

  •  成功时返回0,失败时返回错误码
  •  mutex  指向要初始化的互斥锁对象
  •  如果无法获得锁,任务阻塞

 

释放锁

 #include  <pthread.h>
 int  pthread_mutex_unlock(pthread_mutex_t *mutex);

  •  成功时返回0,失败时返回错误码
  •  mutex  指向要初始化的互斥锁对象
  •  执行完临界区要及时释放锁

 

示例

宏 _LOCK_ 为1表示使用互斥机制,保证主进程与子进程执行到临界区不能被切换,所以 value1 始终等于 value2 !

#include <stdio.h>
#include <pthread.h>

unsigned int count, value1, value2;
pthread_mutex_t lock;

void *function(void *arg);
int main(void) {
  pthread_t  a_thread;

  if (pthread_mutex_init(&lock, NULL) != 0) {
    printf(“fail to pthread_mutex_init\n”);  exit(-1);
  }
  if (pthread_create(&a_thread, NULL, function, NULL) != 0) {
    printf(“fail to pthread_create”);  exit(-1); 
  }
  while ( 1 ) {
    count++;
#ifdef  _LOCK_
    pthread_mutex_lock(&lock);
#endif
    value1 = count;
    value2 = count;
#ifdef  _LOCK_
    pthread_mutex_unlock(&lock);
#endif
  }
  
  return 0;
}

void *function(void *arg) {
  while ( 1 ) {
#ifdef  _LOCK_
    pthread_mutex_lock(&lock);
#endif
    if (value1 != value2) {
      printf(“value1 = %u, value2 = %u\n”, value1, value2);
      usleep(100000);
    }
#ifdef  _LOCK_
    pthread_mutex_unlock(&lock);
#endif
  }
  
  return  NULL;  
}