TCP/IP网络编程——理解线程(临界区、同步)

每个进程的内存空间都有保存全局变量的“数据区”,动态分配内存空间的堆,函数运行时使用的栈。

进程的切换需要上下文切换,开销巨大。

TCP/IP网络编程——理解线程(临界区、同步)_第1张图片

线程共享数据区和堆,只需分离栈区域,上下文切换时开销较小。

TCP/IP网络编程——理解线程(临界区、同步)_第2张图片

下面是多线程求1-10的和的程序:

thread.c

//多进程模型缺点:创建进程过程会带来一定开销(频繁的上下文切换);数据交换需要IPC技术
//上下文切换:如果运行进程A后要切换到运行进程B,就应将A的相关信息移出内存,并读入B的相关信息
//多线程模型优点:上下文切换不需要切换数据区和堆,可以利用数据区和堆交换数据
#include 
#include 

void * thread_summation(void *arg);
int sum=0;

int main(void)
{
  pthread_t id_t1, id_t2;	//线程ID
  int range1[]={1, 5};
  int range2[]={6, 10};
  
  //创建线程
  pthread_create(&id_t1, NULL, thread_summation, (void*)range1);
  pthread_create(&id_t2, NULL, thread_summation, (void*)range2);
  //等待线程终止
  pthread_join(id_t1, NULL);
  pthread_join(id_t2, NULL);
  
  printf("result: %d \n", sum);
  return 0;
}

void* thread_summation(void* arg)
{
  int start=((int *)arg)[0];
  int end=((int *)arg)[1];
  
  while(start<=end)
  {
    sum+=start;	//此处存在临界区相关问题
    start++;
  }
  return NULL;
}

程序执行流程图如下图所示:

TCP/IP网络编程——理解线程(临界区、同步)_第3张图片

但是上述程序存在一个问题:

“2个线程会同时访问全局变量sum”

如下图所示,线程1首先读取该变量num的值并将其传给CPU,获得加1后的结果是100,最后再把结构写回变量num,这样num中就保存100。

TCP/IP网络编程——理解线程(临界区、同步)_第4张图片

但是线程2有可能在线程1还未写入num时拿到num(此时num为99),这样一来最终num的结果就不是101,而是100了(线程12都将100写入num)

TCP/IP网络编程——理解线程(临界区、同步)_第5张图片

操作num的这部分代码就叫临界区,临界区:函数内同时运行多个线程引起问题的多条语句构成的代码块。

解决上述问题的方法就是——线程同步

它解决了两个问题:1.同时访问同一内存空间发生的情况 2.需要指定访问同一内存空间的线程执行顺序的情况

针对问题1——互斥量

互斥量相当于对临界区加了一把锁,阻止多个线程同时访问。

mutex.c

//临界区:函数内同时运行多个线程时引起问题的多条语句构成的代码块
//多个线程同时访问临界区会出问题
//解决方法——线程同步:
//1.同时访问同一内存空间时发生的问题——互斥量
//2.需要指定访问同一内存空间的线程执行顺序的情况——信号量
//互斥量好比一个洗手间,同一时刻只允许一个人使用,有加锁和解锁的过程
#include 
#include 
#include 
#include 
#include 

#define NUM_THREAD 100

void * thread_inc(void *arg);
void * thread_des(void *arg);

long long num=0;
pthread_mutex_t mutex;	//创建互斥量

int main(void)
{
  pthread_t thread_id[NUM_THREAD];
  
  pthread_mutex_init(&mutex, NULL);	//初始化
  int i;
  for(i=0;i

针对问题2——信号量

控制访问顺序的同步,参考操作系统中的“生产者——消费者”问题。

semaphore.c

//信号量:控制线程访问顺序的同步
//PV操作:信号量在调用sem_post时增1,调用sem_wait时减1
//在信号量为0时调用sem_wait时线程将进入阻塞状态
//生产者与消费者模型
#include 
#include 
#include 

void * read(void *arg);
void * accu(void *arg);
//定义信号量
static sem_t sem_one;
static sem_t sem_two;
static int num;

int main(void)
{
  pthread_t id_t1, id_t2;
  sem_init(&sem_one, 0, 0);	//初始化sem_one为0
  sem_init(&sem_two, 0, 1);	//初始化为1
  
  pthread_create(&id_t1, NULL, read, NULL);
  pthread_create(&id_t2, NULL, accu, NULL);
  
  pthread_join(id_t1, NULL);
  pthread_join(id_t2, NULL);
  
  sem_destroy(&sem_one);
  sem_destroy(&sem_two);
  return 0;
}

void* read(void* arg)
{
  int i;
  for(i=0;i<5;i++)
  {
    fputs("Input number: ", stdout);
    sem_wait(&sem_two);	//占用盘子sem_two=0
    //临界区
    scanf("%d", &num);
    sem_post(&sem_one);	//在盘子上放苹果sem_one=1
  }
}

void* accu(void* arg)
{
  int sum=0, i;
  for(i=0;i<5;i++)
  {
    sem_wait(&sem_one);	//吃掉苹果sem_one=0
    sum+=num;
    sem_post(&sem_two);	//放出盘子sem_two=1
  }
  printf("result: %d \n", sum);
  return NULL;
}


你可能感兴趣的:(TCP/IP网络编程)