当前位置: 首页 > article >正文

嵌入式学习第二十一天--线程

线程 


进程的创建:
   1. fork --- 复制
      
   2. vfork 虚拟拷贝 
       copy on write //写时拷贝 
     
总结:
   1.进程创建过程 --- 是通过复制 
     导致 创建时 ,需要拷贝大量的数据 --- 影响效率 
   2.线程拷贝
     减少了拷贝的数据量 ---提升创建效率 
   3.进程 是 CPU 调度的基本单位 
    
 

进程(重量级的进程): 更多侧重于 成为 资源分配的单位    ---- 资源分配的基本单位
线程(轻量级的进程): 更侧重于 成为一个 执行单位            ---- 调度执行的最小单位 
线程组成:
 线程id   //long -- 8字节 
 程序计数器 (program counter) //寄存器  --- 8字节  
 其它寄存器  // 51 * 8 字节 = 408字节
 栈  //8M
 

进程的组成:
     text|data|bss|堆栈| + pcb
   
线程 --- 主要侧重 去 执行任务 (资源更多的是共享了进程资源)

线程 和 进程之间的关系:


  1.线程是依附于进程的 
  2.进程不存在,相关的线程也不复存在 
  3.一个进程中,可以创建多个线程 
  4.进一步提高了并发程度 
  
  
        fork
         |
    /        \
father      child 
 /            \
多个线程      多个线程 

linux下如何进行线程编程:

NPTL 函数库 --- 提供了线程的操作 
                  
New     Posix Thread Library //新的posix标准的线程库 
Native                       //本地线程库 
            

NPTL库 --- stdio 


1.创建 
2.执行
3.结束             


  pthread_create //线程创建 
  


 #include <pthread.h>

      int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
      功能:
          创建一个新的线程
      参数:
        @thread --- 线程id 
        @attr   --- 线程属性  //默认属性 NULL --可结合性   
                              //可分离属性 
        @start_routine ---线程执行函数//线程回调函数 --- 提现线程任务执行的部分
        @arg           ---这是传给 start_routine 函数的参数
          
  void * do_something(void *arg)  //线程执行函数
  {
     
  }
     返回值:
        成功   0
        失败  返回错误码 
        
创建出来的线程 --- 次线程 子线程
原先main函数对应执行流 --- 主线程

练习:
    创建两个线程 
    线程1 打印  线程1 ... pid    //getpid
    线程2 打印  线程2 ... pid    //getpid
    
    
    
注:    
    编译
    gcc pthread_creat.c -lpthread
   //链接库     
   
   
           [NPTL 库] <--- tid表示的是在 NPTL库中 对线程的 一个代表
              |           
   -----------|-------------------   getpid
  内核        | 
              |
           do_clone(,SHARED) //线程 --- 进程 
    
    
    
eg:
    定义一个全局变量 
    要求  线程1  做 加 1 
          线程2     加 2
          线程3     加 3

#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>
//int num;
void * do_something1(void *arg)
{
	int *n=(int *)arg;
	while(1)
	{
		(*n)+=1;
		printf("num = %d\n",*n);
		sleep(2);
	}
		return NULL;
}
void * do_something2(void *arg)
{
	int *n=(int *)arg;
	while(1)
	{
		(*n)+=2;
		printf("num = %d\n",*n);
		sleep(2);
	}
		return NULL;
}
void * do_something3(void *arg)
{
	int *n=(int *)arg;
	while(1)
	{
		(*n)+=3;
		printf("num = %d\n",*n);
		sleep(2);
	}
		return NULL;
}

typedef void *(*thread_cb_t)(void*);
int main(int argc, const char *argv[])
{
	thread_cb_t func[3]={do_something1,do_something2,do_something3};;
	pthread_t tid[3];
	int i=0;
	int m=0;
	for(i=0;i<3;i++)
	{
		int ret;
		if((ret=pthread_create(&tid[i],NULL,func[i],&m))!=0)
		{
			errno=ret;
			perror("pthread_create fail");
			return -1;
		}
	}
	while(1)
	{
		printf("--------main------m=%d\n",m);
		sleep(2);
	}
	return 0;
}


          
好处:          
     线程共享数据方便 
      --- 带来 ---竞争问题 
      

线程结束方式:


 The new thread terminates in one of the following ways:

       * It calls pthread_exit(3), 
         specifying an exit status value that is available to another thread 
         in the same process that calls pthread_join(3).

       * It returns from start_routine(). 
           This is equivalent to calling pthread_exit(3) with the value supplied in the return statement.

       * It is canceled (see pthread_cancel(3)).

       * Any of the threads in the process calls exit(3), or the main thread performs a return from main(). 
       This causes the termination of all threads in the process.

pthread_exit 


void pthread_exit(void *retval);
功能:
    结束线程 
参数:
   retval --- 带出的值的 地址 
 
注意:
   pthread_exit //带出的是 保存了 退出状态值  的空间的地址
                //退出状态值 不能放在栈上 
                

int pthread_join(pthread_t thread, void **retval);


int pthread_cancel(pthread_t thread) 


功能:
    取消线程 
参数:
  thread --- 要取消的线程的tid 
返回值 
   成功 0 
   失败 错误码 
   
注意:
  1. 线程间 的地位是平等的 可以相互取消 
  


总结:


 线程:
    1.线程 --- 轻量级的进程 
    2.线程组成 
      线程tid 
      程序计数器 
      其它寄存器 
      栈 
    3.创建线程 
      pthread_create 
         tid 
         属性 -- 可结合 可分离 ---决定最终资源的回收方式  
         线程执行函数 --- 这是体现线程任务的部分 
         arg  -- 传给线程执行函数的参数 
   4.线程退出 
     a.pthread_exit 
     b.return ---线程执行函数中 
     c.pthread_cancel 
     d.exit 
   
   5.线程退出状态值 
     pthread_join 


eg:
     多线程拷贝文件 

#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>


typedef struct
{
	int fd_s;
	int fd_d;
	int size;
	int len;
	int id;
}msg_t;
#if 0
void * do_copy (void *arg)
{
	msg_t *p = arg;

	lseek(p->fd_s,p->size*p->id,SEEK_SET);
	lseek(p->fd_d,p->size*p->id,SEEK_SET);

	printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p->id,p->fd_s,p->fd_d,p->size,p->len);

	char buf[p->len];
	int ret = read(p->fd_s,buf,p->len);
	write(p->fd_d,buf,ret);

	return NULL; 
}
#endif

void * do_copy (void *arg)
{
	msg_t p = *(msg_t*)arg;

	lseek(p.fd_s,p.size*p.id,SEEK_SET);
	lseek(p.fd_d,p.size*p.id,SEEK_SET);

	printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p.id,p.fd_s,p.fd_d,p.size,p.len);

	char buf[p.len];
	int ret = read(p.fd_s,buf,p.len);
	write(p.fd_d,buf,ret);

	return NULL; 
}

//cp src dest 
int main(int argc, const char *argv[])
{
	if (argc!=3)
	{
		printf("Usage: %s <src> <dest>\n",argv[0]);
		return -1;
	}
	
	int fd_s = open(argv[1],O_RDONLY);
	int fd_d = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666);
	if (fd_s < 0 || fd_d < 0)
	{
		perror("open fail");
		return -1;
	}

	int n = 0;
	printf("Input threads num: ");
	scanf("%d",&n);

	int i = 0;
	int ret = 0;
	pthread_t tid[n];
	
	msg_t msg[n];
	struct stat st;

	if (stat(argv[1],&st) < 0)
	{
		perror("stat fail");
		return -1;
	}
	int f_len = st.st_size;

	
	for (i = 0; i < n; ++i)
	{
	//	msg.fd_s = fd_s;
	//	msg.fd_d = fd_d;
	//	msg.size = f_len / n;
	//	msg.id = i;
		msg[i].fd_s = fd_s;
		msg[i].fd_d = fd_d;
		msg[i].size = f_len / n;
		msg[i].id = i;

	  
		if (i == n-1)
		{ 
			msg[i].len  = f_len - (f_len/n)*(n-1);
		}else 
		{
			msg[i].len  = f_len/n;
		}

		ret = pthread_create(&tid[i],NULL,do_copy,&msg[i]);

		if (ret != 0)
		{
			errno = ret;
			perror("pthread_create fail");
			return -1;
		}

//		sleep(1);
	}


	printf("----main-----\n");
	for (i = 0; i < n; ++i)
		pthread_join(tid[i],NULL);

	close(fd_s);
	close(fd_d);
	
	return 0;
}


     
   
 

线程的属性的设置:


1. pthread_detach  //将指定的线程设置为分离状态 --- 那么资源的回收将自动完成
2. pthread_attr_setdetachstate   

   int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
   功能:
       设置线程的属性 
   参数:
       @attr --- 属性的对象 (变量)
       @detachstate --- 要设置的属性 
            PTHREAD_CREATE_DETACHED  //分离
             PTHREAD_CREATE_JOINABLE //结合 
  返回值
     成功 0
     失败 errno 
     //1.初始化 一个 属性的对象 
      int pthread_attr_init(pthread_attr_t *attr);
 
     //2.设置属性信息      
      int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
     //3.销毁      
     int pthread_attr_destroy(pthread_attr_t *attr);
 

atexit()


atexit();  //注册退出清理函数 
           //进程结束 
           //return  //main
           //exit 



void pthread_cleanup_push(void (*routine)(void *), void *arg);

//线程退出清理函数:

    功能:注册一个线程清理函数
    参数,routine,线程清理函数的入口
                arg,清理函数的参数。
    返回值,无


        
void pthread_cleanup_pop(int execute);


    功能:调用清理函数
    execute,
            非0  执行清理函数
            0 ,不执行清理            
    返回值,无
    
注意:
   1.     使用时 pthread_cleanup_push  pthread_cleanup_pop 要一起用 ,需要在一个代码块 
 
    
触发方式:
   1.pthread_cleanup_pop(非零值)
   2.pthread_cleanup_pop(0) //pthread_exit() //退出动作会导致 触发 
    pthread_exit(NULL);
    pthread_cleanup_pop(0); 
   3.pthread_cancel(); //被其它线程结束时 
   


eg:
    定义要给全局变量 
    int cnt = 0;
    创建两个线程 --- 执行50000 
    每个线程 
    int temp = cnt;
    printf("cnt = %d\n",cnt);
    temp = temp + 1;
    cnt = temp;

#include<stdio.h>
#include<errno.h>
#include<pthread.h>
int cnt =0;
pthread_mutex_t mutex;
void*dosomething1(void*arg)
{
	int n=50000;
	while(n)
	{	pthread_mutex_lock(&mutex);
		int temp =cnt;
		printf("cnt = %d\n",cnt);
		temp = temp+1;
		cnt = temp;
		pthread_mutex_unlock(&mutex);
		--n;
	}
	return NULL;
}
void*dosomething2(void*arg)
{
	int n=50000;
	while(n)
	{	
	    pthread_mutex_lock(&mutex);
		int temp =cnt;
		printf("cnt = %d\n",cnt);
		temp = temp+1;
		cnt = temp;
	    pthread_mutex_unlock(&mutex);
		--n;
	}
	return NULL;
}
typedef void*(*pthread_cb_t)(void *);
int main(int argc, const char *argv[])
{
	pthread_t pid[2];
	int i=0;
	int ret;
	pthread_cb_t func[2]={dosomething1,dosomething2};
	for(i=0;i<2;i++)
	{
		ret = pthread_create(&pid[i],NULL,func[i],NULL);
		if(ret!=0)
		{
			errno = ret;
			perror("pthread_create fail");
			return -1;
		}
		
	}
	printf("-----------main-----------\n");
	pthread_join(pid[0],NULL);
	pthread_join(pid[1],NULL);
	return 0;
}

线程 对比 进程 

进程 
   优点
      进程空间独立 --- 更稳定 安全 可靠      
   缺点
      进程创建,调度效率低
      数据共享不方便 ---- 进程间通信
    
线程 
   优点
     创建,调度效率高 
     共享数据方便 
   缺点
      共享进程空间和资源 
      ---不稳定,不安全 
      共享数据 ---资源的竞争 
    
线程进阶:

   全局的cnt  ---- 共享资源(公共资源) --- 临界资源 
   临界区 --- 访问临界资源代码段 
   

   
   互斥操作  


   互斥 --- 排他性 
   原子性操作 --- 不可再分的操作 
     
    临界资源: 共享资源 
    临界区  : 一段代码区域(访问临界资源的那段代码)
    原子操作: 要么不操作,要操作,一定是一次完整的操作。不能被打断。
    
    概念:互斥 ===》在多线程中对临界资源的排他性访问。

    互斥机制 ===》互斥锁  ===》保证临界资源的访问控制。

    pthread_mutex_t   mutex;
    互斥锁类型        互斥锁变量 内核对象

    框架:


       定义互斥锁 ==》初始化锁 ==》加锁 ==》解锁 ==》销毁
        ****                       ***      *** 
 
      //互斥锁 互斥量 
       pthread_mutex_init(); //初始化一把锁 
       pthread_mutex_lock(); //上锁 
       pthread_mutex_unlock(); //解锁 
       pthread_mutex_destroy();//销毁一把锁 
   


     1、定义:


        pthread_mutex_t   mutex;

     2、初始化锁


        int pthread_mutex_init(
            pthread_mutex_t *mutex,
            const pthread_mutexattr_t *attr);
        功能:将已经定义好的互斥锁初始化。
        参数:mutex 要初始化的互斥锁
              atrr  初始化的值,
              一般是NULL表示默认锁   //一般的锁 ,读写锁 
        返回值:成功 0
                失败 非零
        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态初始化 


     3、加锁:


        int pthread_mutex_lock(pthread_mutex_t *mutex);
        功能:用指定的互斥锁开始加锁代码
              加锁后的代码到解锁部分的代码属于原子操作,
              在加锁期间其他进程/线程都不能操作该部分代码
              如果该函数在执行的时候,mutex已经被其他部分
              使用则代码阻塞。

        参数: mutex 用来给代码加锁的互斥锁
        返回值:成功 0
                失败 非零

     4、解锁


        int pthread_mutex_unlock(pthread_mutex_t *mutex);
        功能:将指定的互斥锁解锁。
              解锁之后代码不再排他访问,一般加锁解锁同时出现。
        参数:用来解锁的互斥锁
        返回值:成功 0
                失败 非零

     5、销毁


         int pthread_mutex_destroy(pthread_mutex_t *mutex);
         功能:使用互斥锁完毕后需要销毁互斥锁
         参数:mutex 要销毁的互斥锁
         返回值:成功  0
                 失败  非零
 
注意:
   原则 --- 锁的区域 尽可能小 
          

eg:
   创建两个线程
   线程1 打印 hello
   线程2 打印 world

      

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


void* print_hello(void *arg)
{
	while (1)
	{
		printf("hello ");
	}
}


void* print_world(void *arg)
{
	while (1)
	{
		printf("world\n");
	}
}



int main(int argc, const char *argv[])
{

	pthread_t tid[2];

	int ret = 0;

	if ((ret = pthread_create(&tid[0],NULL,print_hello,NULL)) != 0)
	{
		errno = ret;
		perror("pthread_create fail");
		return -1;
	}
	if ((ret = pthread_create(&tid[1],NULL,print_world,NULL)) != 0)
	{
		errno = ret;
		perror("pthread_create fail");
		return -1;
	}

	printf("--main--\n");


	pthread_join(tid[0],NULL);
	pthread_join(tid[1],NULL);


	return 0;
}


       
 


   


http://www.kler.cn/a/560020.html

相关文章:

  • 基于CNN的FashionMNIST数据集识别3——模型验证
  • Java多线程与高并发专题——深入synchronized
  • PythonWeb开发框架—Django之DRF框架的使用详解
  • ai-1、人工智能概念与学习方向
  • 商业化运作的“日记”
  • system运行进程以及应用场景
  • 【Python爬虫(61)】Python金融数据挖掘之旅:从爬取到预测
  • 【odoo18-文件管理】在uniapp上访问odoo系统上的图片
  • 第二个接口-分页查询
  • 网站快速收录:如何优化网站图片Alt标签?
  • 如何安装vm和centos
  • 基于 IMX6ULL 的环境监测自主调控系统
  • github如何创建空文件夹
  • 图像处理篇---图像处理中常见参数
  • 基础学科与职业教育“101计划”:推动教育创新与人才培养
  • Windows逆向工程入门之逻辑运算指令解析与应用
  • 湖北中医药大学谱度众合(武汉)生命科技有限公司研究生工作站揭牌
  • 异常(1)
  • 如何在java中用httpclient实现rpc post 请求
  • linux-多进程基础(1) 程序、进程、多道程序、并发与并行、进程相关命令,fork