概述
1线程(thread)的概念和特点
网络一般都需要实现代码的并行。代码的并行必须借助多进程/多线程。
主流操作系统中都是支持多进程,而在每个进程的内部,都支持多线程并行。
进程,重量级的,拥有自己独立的内存空间。
线程,轻量级的,不需要拥有自己独立的内存空间,只是额外拥有一个独立的栈。一个进程内存的所有线程共享进程的资源(代码区、全局区、堆、文件目录……)。
进程中支持多线程并行,其中有一个是主线程,进程中必须有主线程(main函数)。
因此网络开发经常是网络+多线程模式。
2 线程的实现原理
计算机程序运行的硬件必备:CPU、内存。如果要并行,意味着CPU和内存都应该可分。内存是可分的,但CPU不可分,那么多线程怎么并行?
主流操作系统采用CPU时间片技术实现多线程的并行。人的感知是需要时间的,这种时间属于时间段,比如0.1秒,对于计算机来说,0.1秒可以分为100毫秒。把100毫秒的CPU执行时间分成100个CPU时间片,每个 1毫秒。假如有4个线程并行,每个线程分1片,4毫秒以后,每个线程都运行了1毫秒。针对时间点来说,线程没有并行;针对时间段来说,利用CPU时间片技术可以实现并行。
多线程之间互相独立,但又互相影响。
主线程一旦结束,进程随之结束,进程结束导致所有线程结束。
多线程之间代码是乱序执行,每个线程内部的代码是顺序执行。
3 线程的实现
POSIX规范中对线程做了比较完善的定义,因此,线程编码使用 pthread.h,几乎所有的函数都以pthread_ 开头。代码在libpthread.so中。
比如:创建线程的函数:
pthread_create()
4个指针类型做参数:
第一个参数:用于存储pthread_t 类型的线程ID
第二个参数: 线程属性,一般为0即可(默认属性)
第三个参数和第四个参数联合使用,第三个参数是函数指针,把线程需要执行的代码写在函数中,函数的参数由第四个参数提供。
void* (*fun) (void*)
返回,成功返回0,失败返回错误码。线程的函数错误处理通过返回错误码的方式,而不是使用errno。
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void* task(void* p){
int i;
for(i=0;i<100;i++){
printf("task:%dn",i);
}
}
int main(){
pthread_t id;//用来存储线程ID
printf("size=%dn",sizeof(id));
int res = pthread_create(&id,0,task,0);
if(res/*!=0*/)
printf("create error:%sn",strerror(res));//线程错误处理
int i;
for(i=0;i<100;i++){
printf("main:%dn",i);
}
sleep(1);
}
4 线程的参数和返回值
4.1 线程的参数
在使用线程的参数时,必须保证参数的指向有效。
pthread_join()可以让一个线程等待另外一个线程结束,并且取得结束线程的返回值。(类似wait)
#include <stdio.h>
#include <pthread.h>
void* task(void* p){//p就是create()第4个参数
int* pi = p;
printf("*pi=%dn",*pi);
*pi = 200;
}
//练习:线程传入圆的半径,打印圆的面积
void* task2(void* p){
double* pd = p;
printf("s=%lfn",3.14*(*pd)*(*pd));
}
int main(){
pthread_t id1,id2,id3;
int x = 100;
pthread_create(&id1,0,task,&x);
id2 = pthread_self();//取当前线程的ID
printf("id1=%u,main=%un",id1,id2);
pthread_join(id1,0);
printf("x=%dn",x);
double d = 1.0;
pthread_create(&id3,0,task2,&d);
pthread_join(id3,0);
}
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
void* task(void* p){
int x = (int)p;//可以拿指针当int用
printf("x=%dn",x);
}
void* task2(void* p){
sleep(1);
int* pi = p;//p已经被释放,无效
printf("*pi=%dn",*pi);
}
int main(){
pthread_t id1,id2;
int x = 100;
pthread_create(&id1,0,task,(void*)x);//传int
pthread_join(id1,0);
int* pi = malloc(4); *pi = 100;
pthread_create(&id2,0,task2,pi);
free(pi); pthread_join(id2,0);
}
4.2 线程的返回值
关于函数/线程的返回值:
1 不能直接以数组做返回类型;
2 能返回局部变量,但不能返回指向局部变量的指针;
3 加了static的变量指针可以返回。
线程的返回值必须是一个有效的指针:全局变量、常量、传入的指针、static的局部变量。
线程的返回值可以pthread_join的第二个参数取得:
pthread_join(pthread_t id,void** retval)
取返回值时,相当于代码:
*(retval) = 线程的返回值
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void* task(void* p){
printf("%sn",(char*)p);
//p = "hello";//改地址,p指向只读常量区
strcpy(p,"hello");//没有改地址main()有效
//char st[] = "hello";//局部变量,返回无效
//return st;
return p;//res = p;
}
//练习:在线程中计算1-10的和,并返回给main
void* task2(void* p){
/*static*/ int sum = 0;
int i;
for(i=1;i<11;i++){
sum = sum+i;
}
return (void*)sum;//∑
}
int main(){
char str[] = "abcde"; pthread_t id;
pthread_create(&id,0,task,str);
char* res;//res是 野指针
pthread_join(id,(void**)&res);//res = p;
printf("res=%sn",res);
pthread_create(&id,0,task2,0);
//int* pi;
//pthread_join(id,(void**)&pi);
//printf("*pi=%dn",*pi);
int x;
pthread_join(id,(void**)&x);
printf("x=%dn",x);
}
5 线程的结束
正常结束:
线程函数结束
pthread_exit(void* retval),与return一样
非正常结束:
出错/被其他线程取消
注:exit()结束的是进程,所以不能用于结束线程。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* task(void* p){
int i;
for(i=0;i<100;i++){
//if(i == 12) return (void*)i;
//if(i == 12) pthread_exit((void*)i);
if(i == 12) exit(i);//结束进程
}
}
int main(){
pthread_t id;
pthread_create(&id,0,task,0);
int res;
pthread_join(id,(void**)&res);
printf("res=%dn",res);
}
6 线程的状态
线程在启动后,可以通过不同的函数进入不同的状态:
pthread_join() 进入非分离状态(同步),非分离状态的线程会在pthread_join()结束后回收线程资源。
pthread_detach() 进入分离状态(异步),分离状态的线程会在线程结束后直接回收线程的资源。
已经处于分离状态的线程 join()没有效果。线程最好处于这两种状态其中的一种。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* task(void* p){
int i;
for(i=0;i<10;i++){
printf("task:%dn",i); usleep(100000);
}
}
int main(){
pthread_t id;
pthread_create(&id,0,task,0);
pthread_detach(id);
pthread_join(id,0);
int i;
for(i=0;i<10;i++){
printf("main:%dn",i); usleep(100000);
}
}
7 线程的取消(了解)
线程的取消就是给目标线程发CANCEL信号,目标线程可以做出3种选择:忽略、立刻停止、过一会再停止。
线程取消的相关函数:
pthread_cancel() 给目标线程发取消信号
pthread_setcancelstate() 设置是否支持取消
pthread_setcanceltype() 设置取消的方式
#include <stdio.h>
#include <pthread.h>
void* task1(void* p){
//pthread_setcancelstate(//不能取消
//PTHREAD_CANCEL_DISABLE,0);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
//pthread_setcanceltype(//立即取消
//PTHREAD_CANCEL_ASYNCHRONOUS,0);
pthread_setcanceltype(//到下一个取消点
PTHREAD_CANCEL_DEFERRED,0);
while(1)
printf("-----------n"),usleep(1);
}
void* task2(void* p){
sleep(3);
printf("取消线程1n");
pthread_cancel(*(pthread_t*)p);
}
int main(){
pthread_t id1,id2;
pthread_create(&id1,0,task1,0);
pthread_create(&id2,0,task2,&id1);
pthread_join(id1,0); pthread_join(id2,0);
}
最后
以上就是体贴香氛为你收集整理的线程初步(1)—— 线程的创建、参数和返回值、结束、状态、取消1线程(thread)的概念和特点2 线程的实现原理3 线程的实现4 线程的参数和返回值5 线程的结束6 线程的状态7 线程的取消(了解)的全部内容,希望文章能够帮你解决线程初步(1)—— 线程的创建、参数和返回值、结束、状态、取消1线程(thread)的概念和特点2 线程的实现原理3 线程的实现4 线程的参数和返回值5 线程的结束6 线程的状态7 线程的取消(了解)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复