Linux初学第十三天<线程一、线程的创建/退出>

一、线程(thread)概述

        啥是线程?如果要概括的话,它是操作系统所能调度的最小单位,被包含在进程当中,进程实际在执行的时候,就是在执行一个线程。典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。
        线程和进程有很大的区别:
        1. 进程有独立的地址空间,线程没有单独的地址空间;
        2. 一个进程崩溃后,在保护模式下不会对其它进程产生影响,而一个线程死掉就等于整个进程死掉;
        3. 多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
        4 多任务时,进程操作间通信操作复杂,需要大量的API去支持,而线程间是共享内存空间的,不需要过多的API,操作简单。
        总而言之,进程是一种"昂贵"的多任务工作方式。多线程和进程相比,它是一种非常"节俭"的多任务操作方式。当然,线程也有自己的缺点:当对数据进行使用时,两个线程之间不能同时使用同一个数据,不然会发生数据紊乱

二、相关API

        线程相关的API有十几个,它们都放在 <pthread.h> 头文件中:
在这里插入图片描述
今天只学习前三个:创建函数、退出函数和等待函数。和pthread_self()获取线程ID函数

1.创建 pthread_create()

函数原型: int pthread_create()(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
参数说明:
              1. tidp 指向新线程的ID,可以理解这个就是创建后的线程ID,和pid是一样的性质;
              2. attr 用于定制各种不同的线程属性,NULL为创建默认属性的线程;
              3. *start_rtn(void *) 这是新线程要去执行的函数;
              4. arg 是需要传给 *start_rtn(void *) 函数的参数。
返回值: 成功返回0;失败返回错误编号
用法举例:

#include <pthread.h>
void *Start_Rtn(void *arg)
{
	....//执行一些东西
}
int main()
{
	pthread_t T1;//线程ID
	int arg=100; //需要传给上面那个函数的*arg的东西
	int pthread_ret=pthread_create(&T1,NULL,Start_Rtn,(void *)&arg);
}

2.退出 pthread_exit()

函数原型: int pthread_exit(void *rval_ptr);
参数说明: *rval_ptr为退出时,需要传递给pthread_join的数据;不传数据用 NULL
返回值: 成功返回0;失败返回错误编号
用法举例:

void *Start_Rtn(void *arg)
{
	int val=33;
	....//执行一些东西
	pthread_exit(&val);
}

3.等待 pthread_ join()

函数原型: int pthread_ join (pthread_t thread, void **rval_ptr);
参数说明:
              1. thread为需要等待的线程ID;
              2. **rval_ptr 指向 pthread_exit返回的数据;
返回值: 成功返回0;失败返回错误编号
用法举例:

#include <pthread.h>
void *Start_Rtn(void *arg)
{
	int val=33;
	....//执行一些东西
	pthread_exit(&val);
}
int main()
{
	pthread_t T1;//线程ID
	int arg=100; //需要传给上面那个函数的*arg的东西
	int *val=NULL;
	int pthread_ret=pthread_create(&T1,NULL,Start_Rtn,(void *)&arg);
	
	pthread_join(T1,(void **)&val);
}

4.获取ID pthread_ self()

函数原型: pthread_t pthread_self (void);
返回值: 返回进程ID
用法举例:

#include <pthread.h>
void *Start_Rtn(void *arg)
{
	....//执行一些东西
}
int main()
{
	pthread_t T1;//线程ID
	int arg=100; //需要传给上面那个函数的*arg的东西
	int pthread_ret=pthread_create(&T1,NULL,Start_Rtn,(void *)&arg);
	printf("main id=%ld\n",(unsigned long)pthread_self());
}

        还是那个配方,还是那个套路,学完API就写程序。

三、小实验

1.创建线程:

        编写一个程序,实现子线程打印自身ID:

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

// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                         // void *(*start_routine) (void *), void *arg);
//子线程处理函数
void *StartRoutine(void *Arg)
{
        printf("T1:Is ID=%ld\n",(unsigned long)pthread_self());//打印子线程的ID
        printf("T1:Is Arg=%d\n",*((int *)Arg));//打印创建子线程时传过来的数据,因为Arg传过来时是void 型的指针,所以需要需要强制转换为int 型,再取地址
}
int main()
{
        pthread_t T1;//定义一个线程ID
        int Thread_ret;//创建线程时返回值
        int Arg=100;   //需要传给子线程的参数

        Thread_ret=pthread_create(&T1,NULL,StartRoutine,(void *)&Arg);//创建子线程
        if(Thread_ret==0){
                printf("Thread creat succes,Is ID=%ld\n",(unsigned long)pthread_self());//打印主线程ID
        }
        sleep(1);//主线程睡眠1秒,给子线程充分的执行时间
        return 0;
}

        解释一下那个sleep 函数的作用,博主在做实验的时候,如果不加sleep 函数或者while(1)大循环等待的话,子线程会大概率执行不了,或者执行到一半就退出程序了。那是因为两个线程同步执行,父线程可能会比子线程先退出程序,所以导致子线程执行不了,但只要在主线程等待一下子线程的话,就能正常执行了。注意: pthread.h 并不是标准C库的范畴,所以编译时要加上 -pthread,否则会出错
运行结果:
在这里插入图片描述

2.等待线程并打印退出时的数据

        编写程序,创建两个线程,主线程等待两个子线程的退出,并打印出两个子线程返回的值。

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

// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                         // void *(*start_routine) (void *), void *arg);
static char *trval;//共享内存的变量
//线程1处理函数
void *StartRoutine(void *Arg)
{
        trval="This is child 1";
        printf("T1:Is ID=%ld\n",(unsigned long)pthread_self());
        printf("T1:Is Arg=%d\n",*((int *)Arg));
        pthread_exit(trval);
}
//线程2处理函数
void *StartRoutine1(void *Arg)
{
        trval="This is CHiled 2";
        printf("T2:Is ID=%ld\n",(unsigned long)pthread_self());
        printf("T2:Is Arg=%d\n",*((int *)Arg));
        pthread_exit(trval);

}
int main()
{
        pthread_t T1,T2;
        int Thread_ID;
        int Arg=100;
        char *rval=NULL;
        char *rval2=NULL;
        Thread_ID=pthread_create(&T1,NULL,StartRoutine,(void *)&Arg);//创建线程1

        Thread_ID=pthread_create(&T2,NULL,StartRoutine1,(void *)&Arg);//创建线程2

        pthread_join(T1,(void **)&rval);//等待线程1
        pthread_join(T2,(void **)&rval2);//等待线程2
      
        printf("main:T1 rval=%s\n",rval);
        printf("main:T2 rval=%s\n",rval2);

        return 0;
}

        上面的程序中,定义了一个共享的指针变量trval,两个线程同时对它赋值,我们看看运行结果:
运行结果:
在这里插入图片描述
        运行结果中,进程会优先执行子线程,然后再去执行父线程。而在执行两个子线程的过程中,线程T1和T2打印的值有时会是一样的,那是因为T1在执行的过程中,T2也在执行,也在改变trval的数据 。这个Buf会在明天的学习中解决,明天开始学习互斥锁的概念及使用。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用什么样的思路可以写出<!DOCTYPE html> <html> <head> <title>计算器</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <h1>计算器</h1> <input type="text" id="result" readonly> <br> <button onclick="clearResult()">清除</button> <button onclick="deleteChar()">删除</button> <br> <button onclick="addToResult('7')">7</button> <button onclick="addToResult('8')">8</button> <button onclick="addToResult('9')">9</button> <button onclick="addToResult('+')">+</button> <br> <button onclick="addToResult('4')">4</button> <button onclick="addToResult('5')">5</button> <button onclick="addToResult('6')">6</button> <button onclick="addToResult('-')">-</button> <br> <button onclick="addToResult('1')">1</button> <button onclick="addToResult('2')">2</button> <button onclick="addToResult('3')">3</button> <button onclick="addToResult('*')">*</button> <br> <button onclick="addToResult('0')">0</button> <button onclick="addToResult('.')">.</button> <button onclick="calculate()">=</button> <button onclick="addToResult('/')">/</button> <br> <script src="calculator.js"></script> <script> let result = document.getElementById('result'); function addToResult(char) { result.value += char; } function clearResult() { result.value = ''; } function deleteChar() { result.value = result.value.slice(0, -1); } function calculate() { try { result.value = eval(result.value); } catch (error) { result.value = 'Error'; } } </script> </body> </html>这段代码
06-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值