• 《Unix/Linux系统编程》学习笔记7


    第四章 并发编程

    一、知识点归纳

    (一)并行计算导论

    1.顺序算法与并行算法

    顺序算法 并行算法

    begin

      step_1

      step_2

      ……

      step_n

    end

    // next step

    cobegin

      task_1

      task_2

      ……

      task_n

    coend

    // next step

    2.并行性与并发性

    (二)线程

    1.线程的原理

    2.线程的优点

    • (1)线程创建和切换速度更快

    • (2)线程的响应速度更快

    • (3)线程更适合并行计算

    3.线程的缺点

    • (1)由于地址空间共享,线程需要来自用户的明确同步。

    • (2)许多库函数可能对线程不安全。

    • (3)在单CPU系统上,使用线程解决问题实际上要比使用顺序程序慢,这是由在运行时创建线程和切换上下文系统开销造成的。

    (三)线程操作

    (四)线程管理函数

    Pthread 库提供了用于线程管理的以下 API。

    pthread_create(thread, attr, function, arg): create thread
    pthread_exit(status)                    : terminate thread
    pthread_cancel(thread)                   : cancel thread
    pthread_attr_init(attr)                   : initialize thread attributes
    pthread_attr_destroy(arr)                 : destroy thread attributes

    1.创建线程

    使用 pthread_create() 函数创建线程。

    int pthread_create(pthread_t *pthread_id, pthread_attr_t *attr, void *(*func)(void *),void *arg);

      如果成功则返回0,如果失败则返回错误代码。pthread_create()函数的参数为

    • pthread_id 是指向 pthread_t 类型变量的指针。它会被操作系统内核分配的唯一线程ID填充。

    2.线程ID

    3.线程终止

    4.线程连接

    (五)线程示例程序(见实践内容)

    1.用线程计算矩阵的和

    2.用线程快速排序

    (六)线程同步

    • 竞态条件:当多个线程试图修改同一共享变量或数据结构时,修改结果取决于线程的执行顺序。

    1.互斥量

      最简单的同步工具是锁,它允许执行实体仅在有锁的情况下才能执行。在 Pthread 中,锁被称为互斥量,意思是相互排斥。互斥变量是用 pthread_mutex_t 类型声明,在使用之前必须对它们进行初始化。有两种方法可以初始化互斥量。

    1. 一种是静态方案,如:pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER 定义互斥量 m,并使用默认属性对其进行初始化。
    2. 另一种是动态方案,使用pthread_mutex_init()函数,可通过 attr 参数设置互斥属性,如:pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t, *attr); 通常,attr 参数可以设置为 NULL,作为默认属性。

    2.死锁预防

      互斥量使用封锁协议。如果某线程不能获取互斥量,就会被阻塞,等待互斥量解锁后再继续。在任何封锁协议中,误用加锁可能会产生一些问题。最常见和突出的问题是死锁。死锁是一种状态,在这种状态下,许多执行实体相互等待,因此都无法继续下去。

      与竞态条件类似,死锁决不能存在于并发程序中。有许多方法可以解决可能的死锁问题,其中包括死锁预防、死锁规避、死锁检测和恢复等。在实际系统中,唯一可行的方法是死锁预防,试图在设计并行算法时防止死锁的发生。一种简单的死锁预防方法是对互斥量进行排序,并确保每个线程只在一个方向请求互斥量,这样请求序列中就不会有循环。

      但是,仅使用单向加锁请求来设计每个并行算法是不可能的。在这种情况下,可以使用条件加锁函数 pthread_mutex_trylock() 来预防死锁。如果互斥量已被加锁,则 trylock()函数会立即返回一个错误。在这种情况下,调用线程可能会释放它已经获取的一些互斥量以便进行退避,从而让其他线程继续执行。在上面的交叉加锁示例,我们可以重新设计一个线程,例如 T1,利用条件加锁和退避来预防死锁。

    /*Thread T1*/
    while(1){
        lock(m1);
        if(!trylock(m2))
            unlock(m1);
        else
            break;
    }

    3.条件变量

    4.生产者—消费者问题

    5.信号量

    • 信号量是进程同步的一般机制。

    6.屏障

    7.用并发线程解线性方程式

    8.Linux 中的线程

      与许多其他操作系统不同,Linux 不区分进程和线程。对于 Linux 内核,线程只是一个与其他进程共享某些资源的进程。在 Linux 中,进程和线程都是由 clone() 系统调用创建的,具有以下原型:

    int clone(int (*fn)(void *), void *child_stack, int flags, void *arg)

    可以看出,clone()更像是一个线程创建函数。它创建一个子进程来执行带有 child_stack的函数fn(arg)。flag 字段详细说明父进程和子进程共享的资源。

    (七)用户级线程

    二、问题与解决思路

    (一)问题1:error: conflicting types for ‘qsort’; have ‘void *(void *)’

    解决方案:qsort()这个函数名被占用了,将函数名 qsort修改成其它名字, 比如改成 qsort1

    三、实践内容与截图

    (一)用线程计算矩阵的和

    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #define N 4
    int A[N][N],sum[N];
    
    void *func(void *arg)
    {
      int j,row;
      pthread_t tid=pthread_self();
      row=(int)arg;
      printf("Thread %d [%lu] computes sum of row %d\n",row,tid,row);
      for(j=0;j<N;j++)
        sum[row]+=A[row][j];
      printf("Thread %d [%lu] done: sum[%d] = %d\n",row,tid,row,sum[row]);
      pthread_exit((void*)0);
    }
    
    int main(int argc,char *argv[])
    {
      pthread_t thread[N];
      int i,j,r,total=0;
      void *status;
      printf("Main: initialize A matrix\n");
      for(i=0;i<N;i++)
        {
          sum[i]=0;
          for(j=0;j<N;j++)
    	{
    	  A[i][j]=i*N+j+1;
    	  printf("%4d ",A[i][j]);
    	}
          printf("\n");
        }
      printf("Main: create %d threads\n",N);
      for(i=0;i<N;i++)
        {
          pthread_create(&thread[i],NULL,func,(void *)i);
        }
      printf("Main: try to join with thread\n");
      for(i=0;i<N;i++)
        {
          pthread_join(thread[i],&status);
          printf("Main: joined with %d [%lu]: status=%d\n",i,thread[i],(int)status);
        }
      printf("Main: compute and print total sum: ");
      for(i=0;i<N;i++)
        total+=sum[i];
      printf("total = %d\n",total);
      pthread_exit(NULL);
    }

    (二)用并发线程快速排序

    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    
    typedef struct{
      int upperbound;
      int lowerbound;
    }PARM;
    
    #define N 10
    int a[N]={5,1,6,4,7,2,9,8,0,3};
    
    int print()
    {
      int i;
      printf("[ ");
      for(i=0;i<N;i++)
        printf("%d ",a[i]);
      printf("]\n");
    }
    
    void *qsort1(void *aptr)
    {
      PARM *ap,aleft,aright;
      int pivot,pivotIndex,left,right,temp;
      int upperbound,lowerbound;
    
      pthread_t me,leftThread,rightThread;
      me=pthread_self();
      ap=(PARM *)aptr;
      upperbound=ap->upperbound;
      lowerbound=ap->lowerbound;
      pivot=a[upperbound];
      left=lowerbound-1;
      right=upperbound;
      if(lowerbound>=upperbound)
        pthread_exit(NULL);
      
      while(left<right)
      {
        do{left++;}while(a[left]<pivot);
        do{right--;}while(a[right]>pivot);
        if(left<right){
          temp=a[left];
          a[left]=a[right];
          a[right]=temp;
        }
      }
      print();
      pivotIndex=left;
      temp=a[pivotIndex];
      a[pivotIndex]=pivot;
      a[upperbound]=temp;
      aleft.upperbound=pivotIndex-1;
      aleft.lowerbound=lowerbound;
      aright.upperbound=upperbound;
      aright.lowerbound=pivotIndex+1;
      printf("%lu: create left and right threads\n",me);
      pthread_create(&leftThread,NULL,qsort1,(void *)&aleft);
      pthread_create(&rightThread,NULL,qsort1,(void *)&aright);
      pthread_join(leftThread,NULL);
      pthread_join(rightThread,NULL);
      printf("%lu: joined with left & right threads\n",me);
    }
    
    int main(int argc,char *argv[])
    {
      PARM arg;
      int i,*array;
      pthread_t me,thread;
      me=pthread_self();
      printf("main %lu: unsorted array = ",me);
      print();
      arg.upperbound=N-1;
      arg.lowerbound=0;
      printf("main %lu create a thread to do QS\n",me);
      pthread_create(&thread,NULL,qsort1,(void *)&arg);
      pthread_join(thread,NULL);
      printf("main %lu sorted array = ",me);
      print();
    }

    (三)Pthread中的互斥量

    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #define N 4
    int A[N][N];
    
    int total=0;
    pthread_mutex_t *m;
    void *func(void *arg)
    {
      int i,row,sum=0;
      pthread_t tid=pthread_self();
      row=(int)arg;
      printf("Thread %d [%lu] computes sum of row %d\n",row,tid,row);
      for(i=0;i<N;i++)
        sum+=A[row][i];
      printf("Thread %d [%lu] update total with %d : ",row,tid,sum);
      pthread_mutex_lock(m);
      total+=sum;
      pthread_mutex_unlock(m);
      printf("Thread %d : total = %d\n",row,total);
    }
    
    int main(int argc,char *argv[])
    {
      pthread_t thread[N];
      int i,j,r;
      void *status;
      printf("Main: initialize A matrix\n");
      for(i=0;i<N;i++){
          for(j=0;j<N;j++){
    	      A[i][j]=i*N+j+1;
    	      printf("%4d ",A[i][j]);
    	    }
          printf("\n");
      }
      m=(pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
      pthread_mutex_init(m,NULL);
      for(i=0;i<N;i++){
          printf("Main: create thread %d \n",i);
          pthread_create(&thread[i],NULL,func,(void *)i);
      }
      printf("Main: try to join with threads\n");
      for(i=0;i<N;i++){
        pthread_join(thread[i],&status);
        printf("Main: joined with %d [%lu]: status=%d\n",i,thread[i],(int)status);
      }
      printf("Main: total = %d\n",total);
      pthread_mutex_destroy(m);
      pthread_exit(NULL);
    }

  • 相关阅读:
    HttpClient 教程 (四)
    HttpClient 教程 (三)
    HttpClient 教程 (二)
    HttpClient 教程 (一)
    git还原本地提交的某个历史记录
    ExtJS下拉列表使用方法(异步传输数据)
    Struts整合ExtJS
    既有post提交又有get提交时的后台处理办法
    Ajax调用查看页面的后台返回json格式数据
    如何在VS中快速导入新的源码以及文件夹
  • 原文地址:https://www.cnblogs.com/20201212ycy/p/16786079.html
Copyright © 2020-2023  润新知