• UC编程之线程


    线程--隶属于进程,是进程中的程序流。操作系统支持多进程,每个进程内部支持多线程。多线程并行(同时执行)代码。

     进程--重量级的,每个进程都需要独立的内存空间。

     线程--轻量级的,线程不拥有独立的内存资源,共享所在进程的内存资源,

          但每个线程都拥有一个独立的栈区。

     开发程序中,多线程使用的概率更高。

     进程中可以有多个线程,但必须有一个主线程(main函数)

     

     由于CPU不可分,真正意义的并行其实不存在的,针对时间点的并行是不存在 

     的。人的感官都是针对时间段(很少)。主流的操作系统都是使用CPU时间片的 

     方式实现并行。每个CPU时间片都是极少的CPU运行时间。

     

     多线程编程有一套成形的API,属于POSIX规范。

     POSIX规范提供了一个头文件pthread.h 和一个共享库文件lib pthread.so

     因此在编写多线程代码时需要#include<pthread.h>,链接时需要加 -l pthread 

     或 -pthread

     

    如何创建线程?

     关于线程的大多数函数/成员变量都是以pthread_开头。

     int pthread_create(pthread_t *id, const pthread_attr_t *attr,

                     void *(fa) (void *), void *p)

     参数id用于存储新建线程的ID

     attr用于传入新建线程的属性,一般0即可

     fa是函数指针,线程执行的代码写成函数fa并在创建线程时传入

     pfa的参数,如果不需要0即可

     注意:线程的错误处理不使用errno,而是直接返回。

     当同一进程内部有多线程时,每个线程内部的代码顺序执行,而多线程之间的

     代码乱序执行。

     多线程之间 相互独立,但也相互影响。

     主线程结束,进程随之结束;进程结束,进程中所有线程都结束。

     pthread_join函数挂起当前线程直到所等待的线程结束。

     线程的参数由 pthread_create的第4个参数传入,返回值由pthread_join的第二

     个参数获取。

     函数pthread_exit(void*)也可以退出线程并且也有返回值(参数void*就是线程的

     返回值)

     函数exit()退出的是进程(所有线程)pthread_exit()退出单个线程。

     

     线程的运行有两种状态:

       非分离状态--可以用函数pthread_join

       分离状态 --可以用函数pthread_detach

     两者的区别:

       非分离状态的线程资源回收要到pthread_join函数结束以后;分离状态的线程资源马上回收。处于分离状态的线程无法join(join没效果)

    经验:线程启动后,最好设置join或者detach

    实例:

      创建线程:

          1 #include<stdio.h>

          2 #include<pthread.h>

          3 

          4 void* task(void* p){

          5   int i;

          6   for(i=0;i<100;i++){

          7     printf("i=%d ",i);

          8   }

          9 }

         10 

         11 

         12 int main(){//主线程

         13     pthread_t id;

         14     pthread_create(&id,0,task,0);//创建线程

         15     int i;

         16     for(i=0;i<100;i++){

         17       printf("main=%d ",i);

         18     }

         19      sleep(1);

         20      pthread_t id2 = pthread_self();//取当前线程

         21      printf("id=%u,id2=%u ",id);

         22     return 0;

         23 }

     

    等待线程(pthread_join):

          1 #include<stdio.h>

          2 #include<pthread.h>

          3 

          4 void* task(void* p){

          5   int* pi = p;

          6   printf("*pi=%d ",*pi);

          7   *pi =200;

          8 }

          9 int main(){//主线程

         10     pthread_t id1; 

         11     int x =100;

         12     pthread_create(&id1,0,task,&x);//创建线程

         13     pthread_join(id1,0);//主线程等待id1的结束

         14     printf("x=%d ",x);

         15     return 0;

         16 }

     

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<pthread.h>

          4 

          5 void* task(void* p){

          6   printf("%d ",(int)p);

          7 

          8 }

          9 void* task2(void* p){

         10   sleep(1);

         11   int* pi = p;

         12   printf("*pi=%d ",*pi);

         13 }

         14 int main(){

         15   pthread_t id;

         16   int x = 100;

         17   pthread_create(&id,0,task,(void*)x);

         18   pthread_join(id,0);

         19   int* pi = malloc(4);

         20   *pi=300;

         21   pthread_create(&id,0,task2,pi);

         22   free(pi);//free()释放掉了pi,使pi无效,传入task2将无效

         23   pthread_join(id,0);

         24   return 0;

         25  }

     

     线程的取消--线程可以被其他线程取消,有一套API(了解)

          1 #include <stdio.h>

          2 #include <pthread.h>

          3 void* task1(void *p){//线程2取消线程1

          4   //设置可以取消

          5   pthread_setcancelstate(//换成DISABLE不能取消

          6     PTHREAD_CANCEL_ENABLE,0);

          7   //设置取消方式: 立即/下一个取消点

          8   pthread_setcanceltype(//立即,DEFERRED下一个

          9    PTHREAD_CANCEL_DEFERRED/*ASYNCHRONOUS*/,0);

         10   while(1) {

         11     printf("I am superman! ");

         12     usleep(1); } }

         13 void* task2(void *p){

         14   sleep(3); printf("开始取消线程1 ");

         15   pthread_cancel(*(pthread_t*)p);  }

         16 int main(){

         17   pthread_t id1,id2;

         18   pthread_create(&id1,0,task1,0);

         19   pthread_create(&id2,0,task2,&id1);

         20   pthread_join(id1,0); pthread_join(id2,0);

         21   return 0;

        22 }

         

    线程同步--因为多线程之间共享进程的资源,多个线程同时访问相同的资源时需要互相协调,以防止数据出现不一致、不完整的问题,线程之间的协调和通信叫线程同步。

     线程同步有很多解决方案:

      线程同步的思路:访问共享资源时,不能并行,而是串行(一个一个的访问)

      pthread中,提供了互斥量(互斥锁)实现线程同步。

      互斥量的使用步骤:

       1 定义互斥量 pthread_mutex_t lock;

       2 初始化互斥量,方法有二:

         pthread_mutex_init(&lock,0);

         或者在定义的时候用宏同时赋值:

         pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;

       3 加上互斥锁(给访问共享资源的代码加)

         pthread_mutex_lock(&lock) 

       4 访问

       5 解锁  pthread_mutex_unlock(&lock)

       6 释放互斥锁的资源

         pthread_mutex_destroy(&lock)

    互斥量实例:

          1 #include<stdio.h>

          2 #include<pthread.h>

          3 

          4 char* data[5];//存数据

          5 int size = 0; //人数

          6 

          7 pthread_mutex_t lock; //1 定义

          8 //= PTHREAD_MUTEX_INITIALIZER;

          9 

         10 void* task(void* p){

         11  pthread_mutex_lock(&lock);//3 上锁

         12  data[size] = (char*)p;

         13  usleep(100000);

         14  size++;

         15  pthread_mutex_unlock(&lock);//4 解锁

         16 }

         17 

         18 int main(){

         19   pthread_mutex_init(&lock,0);//2 初始化

         20   data[size] = "zhangfei";

         21   size++;

         22   pthread_t id1,id2;

         23   pthread_create(&id1,0,task,"guanyu");

         24   pthread_create(&id2,0,task,"zhaoyun");

         25   pthread_join(id1,0);

         26   pthread_join(id2,0);

         27   int i;

         28   for(i=0;i<size;i++){

         29    printf("%s ",data[i]);

         30   }

         31   pthread_mutex_destroy(&lock);

         32   return 0;

         33 }

    信号量-- 就是一个计数器,控制同时访问共享资源的线程/进程数量。

     如果信号量为1,效果等同于互斥量。

     信号量的使用,已经有了固定的API,步骤:

     1 定义一个信号量(semaphore)

       sem_t sem;

      信号量不属于pthread.h

     2 初始化信号量

       sem_init(&sem,0,最大值);

      第二个参数必须是0,0代表控制线程,其他值代表控制进程,Linux只支持线程。

      第三个参数就是计数的初始值,为1信号量的作用等价于互斥量。

     3 获取一个信号量(信号量减1)

       sem_wait(&sem);

     4 访问共享资源

     5 释放一个信号量(信号量加1)

       sem_post(&sem)

     6 如果不再使用,可以删除信号量

       sem_destroy(&sem)

    信号量实现线程同步实例:

          1 #include<stdio.h>

          2 #include<pthread.h>

          3 #include<semaphore.h>

          4 

          5 char* data[5];//存数据

          6 int size = 0; //人数

          7 

          8 sem_t sem; //1 定义

          9 

         10 void* task(void* p){

         11  sem_wait(&sem);//3 上锁

         12  data[size] = (char*)p;

         13  usleep(100000);

         14  size++;

         15  sem_post(&sem);//4 解锁

         16 }

         17 

         18 int main(){

         19   sem_init(&sem,0,1);//2 初始化

         20   data[size] = "zhangfei";

         21   size++;

         22   pthread_t id1,id2;

         23   pthread_create(&id1,0,task,"guanyu");

         24   pthread_create(&id2,0,task,"zhaoyun");

         25   pthread_join(id1,0);

         26   pthread_join(id2,0);

         27   int i;

         28   for(i=0;i<size;i++){

         29    printf("%s ",data[i]);

         30   }

         31   sem_destroy(&sem);

         32   return 0;

         33 }

    练习:

     控制访问数据库的最大的并行线程数量

     数据库最多运行10个线程同时访问,启动20个线程,看一下信号量控制效果 

          1 #include<stdio.h>

          2 #include<pthread.h>

          3 #include<semaphore.h>

          4 

          5 sem_t sem;

          6 void* task(void* p){

          7     int i =(int)p;

          8     printf("%d个线程启动,申请连接 ",i);

          9     sem_wait(&sem);

         10     printf("%d个线程申请成功 ",i);

         11     sleep(10);

         12     printf("%d个线程完成连接 ",i);

         13     sem_post(&sem);

         14 }

         15 int main(){

         16     sem_init(&sem,0,10);//初始化

         17     int i;

         18     for(i = 0;i<20;i++){

         19        pthread_t id;

         20        pthread_create(&id,0,task,(void*)(i+1));

         21     }

         22     while(1);

         23 //  sem_destroy(&sem);

         24     return 0;

         25 }

     

    死锁:线程之间互相锁定,导致所有线程都无法运行。多线程一定要避免锁死

     避免死锁的经验:

      顺序上锁,反向解锁,不要回调。

     thread1:

     lock(&mutex1);

     ...//1

     lock(&mutex2);//3  thread1阻塞等待mutex2

     ...

     unlock(&mutex2);

     ...

     unlock(&mutex1);

     

     thread2:

     lock(&mutex2);//2

     ...//4

     lock(&mutex1);//5 thread2阻塞等待mutex1

     ...

     unlock(&mutex1);

     ...

     unlock(&mutex2);

    thread1运行到lock(&mutex2)时,如果thread2已经运行了lock(&mutex2)则thread1将阻塞等待thread2thread2运行到lock(&mutex1);时,因为thread1已经运行了lock(&mutex1);thread2将阻塞等待thread1,结构thread1thread2都将永远等待对方,无法结束,形成死锁

     

     

     

     

  • 相关阅读:
    【转载】wpf学习笔记数据绑定9
    【转载】wpf学习笔记5
    paip.c#.net 右键菜单带图标
    SG NPN Reload API V2.1 电话卡充值文档
    paip.url参数格式化.txt
    PAIP.MYSQL数据库比较VC313.TXT
    System.Transactions.Diagnostics.DiagnosticTrace 的类型初始值设定项引发异常。配置系统未能初始化
    paip.提升性能---.net listbox 使用BeginUpdate与EndUpdate
    apache设置域名绑定 以及绑定不起作用的排查.
    paip.php and or 语句用法作用
  • 原文地址:https://www.cnblogs.com/marshhu/p/3258694.html
Copyright © 2020-2023  润新知