线程与进程对比

1.用户空间对比

2.内核空间资源对比

在创建线程时,Linux内核仍然创建一个新的PCB来标识这个线程。内核并不认为进程与线程有差别。

进程是操作系统管理资源的基本单元,线程时Linux系统调度的基本单元。

3.进程线程函数对比

创建线程

int pthread_create (pthread_t *__restrict __newthread,

          __const pathread_attr_t *__restrict __attr,

          void *(*__start_routine) (void *),

                              void *__restrict __arg)

成功返回0,失败返回非0值。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/syscall.h>
struct message
{
int i;
int j;
}; void * hello(struct message *str)
{
printf("child, the tid = %lu, pid = %ld\n", pthread_self(),syscall(SYS_gettid));
printf("the arg.i is %d, arg.j is %d\n", str->i, str->j);
while();
} int main(int argc, char *argv[])
{
struct message test;
pthread_t thread_id;
test.i = ;
test.j = ;
pthread_create(&thread_id, NULL, (void *)*hello, &test); //创建线程
printf("parent, the tid = %lu, pid = %ld\n", pthread_self(),syscall(SYS_gettid));
pthread_join(thread_id, NULL);
}

注意,编译的时候需要加上 -lpthread 

gcc -o 名字 源文件 -lphtread  (无法使用perror打印错误信息,因为不修改系统全局变量errno)

void pthread_exit (void *__retval) :线程退出,与exit()函数类似

int pthread_join (pthread_t __th, void **__thread_return) :阻塞调用当前线程的线程,直到此线程退出。类似于wait函数。

第一个参数:被等待线程ID。必须等待关联线程

第二个参数:用户定义指针,存储被等待线程返回值。

int pthread_detach (pthread_t __th) :设置线程为独立线程。成功返回0.

测试线程退出时全局变量和堆变量:仍然可用

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void *helloworld(char *argc);
int main(int argc, char *argv[])
{
int error;
int *temptr;
pthread_t thread_id;
pthread_create(&thread_id, NULL, (void *)*helloworld, "helloworld");
//测试下面两者是否有区别
printf("*p = %x, p = %x\n", *helloworld, helloworld);
if(error = pthread_join(thread_id, (void**)&temptr))
{
perror("pthread_join");
exit(EXIT_FAILURE);
}
//打印子线程退出时的值
printf("temp = %x, *temp = %c\n", temptr, *temptr);
//修改堆空间 测试是否可用
*temptr = 'd';
printf("%c\n", *temptr);
free(temptr);
return ;
} void *helloworld(char *argc)
{
int *p;
p = (int *)malloc( * sizeof(int));
printf("the message is %s\n", argc);
printf("the child id is %u\n", pthread_self());
memset(p, 'c', );
printf("p = %x\n", p);
pthread_exit(p); //退出线程,堆空间首地址做为返回信息
}

可以看到,函数名 helloworld 和 *helloworld 是一样的。

线程退出时资源释放

void pthread_cleanup_push(void (*routine) (void *), void *arg) :压入清理函数栈,后进先出

void pthread_cleanup_pop(int execute) :参数为0,表示不执行弹出的清理函数;非0执行。

#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
void cleanup()
{
printf("cleanup\n");
}
void *test_cancel(void)
{
pthread_cleanup_push(cleanup, NULL);
printf("test_cancel\n");
while()
{
printf("test message\n");
sleep();
}
pthread_cleanup_pop();
}
int main()
{
pthread_t tid;
//创建线程
pthread_create(&tid, NULL, (void *)test_cancel, NULL);
sleep();
//取消子线程
pthread_cancel(tid);
pthread_join(tid, NULL);
}

取消线程

条件1:线程必须可以被其他线程取消,默认可以

条件2:线程处于可取消点才能被取消。

int pthread_cancel (pthread_t __cancelthread) :向某线程发送取消操作。

int pthread_setcancelstate (int __state, int *__oldstate) :设置当前线程的可取消性, state为新状况,oldstate存储原来的状态。

  PTHREAD_CANCEL_DISABLE为不可取消;

  PTHREAD_CANCEL_ENABLE为可取消。(默认值)

int pthread_setcanceltype (int __type, int *__oldtype) :设置取消类型。

  PTHREAD_CANCEL_ASYNCHRONOUS :可随时执行新的或未决的取消请求

  PTHREAD_CANCEL_DEFERRED :在目标线程到达取消点之前,取消请求处于未决状态。(默认值)

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void *thread_function(void *arg);
int main(int argc, char *argv[])
{
int res;
pthread_t a_thread;
void *thread_result;
//创建线程
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if(res != )
{
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep();
printf("Cancelling thread...\n");
//取消子线程
res = pthread_cancel(a_thread);
if(res != )
{
perror("Thread cancelation failed");
exit(EXIT_FAILURE);
}
printf("Waitint for thread to finish...\n");
//等待子线程结束
res = pthread_join(a_thread, &thread_result);
if(res != )
{
perror("Thread join failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} //新线程执行函数
void *thread_function(void *arg)
{
int i, res, j;
sleep();
//设置为不可取消
res = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if(res != )
{
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
sleep();
printf("thread cancle type is disable, can't cancle this thread\n");
for(i = ; i < ; i++)
{
printf("Thread is running (%d)...\n", i);
sleep();
}
//设置为可取消
res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if(res != )
{
perror("Thread pthread_setcancelstate failed");
exit(EXIT_FAILURE);
}
printf("Now change the canclestate is ENABLE\n");
sleep();
pthread_exit();
}

线程与私有数据

如果需要每个线程有自己私有的全局变量,可以使用同名而不同内存地址的线程私有数据结构,成为私有数据TSD。

int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *)) :创建线程私有数据,地址赋值给key. 所有线程对key可见,但线程可以根据自己的需要,在key中填入不同值,相当于同名不同值。第二个参数表示线程退出时key的资源释放函数。

int pthread_key_delete(pthread_key_t key) :注销一个TSD,不调用destr_sunction?

int pthread_setspecific (pthread_key_t key, const void *pointer) :将pointer的值与key相关联。

void * pthread_getspecific (pthread_key_t key) :读取与key相关联的数据

全局变量测试,不使用TSD的情况。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
//全局变量,初值100
int key = ;
void *helloworld_one(char * argc)
{
printf("the message is %s\n", argc);
//修改值为10
key = ;
printf("key=%d, the child id is %u\n", key, pthread_self());
return ;
}
void *helloworld_two(char * argc)
{
printf("the message is %s\n", argc);
//休眠,让另一个线程先修改值
sleep();
printf("key=%d, the child id is %u\n", key, pthread_self());
return ;
}
int main()
{
pthread_t thread_id_one;
pthread_t thread_id_two;
pthread_create(&thread_id_one, NULL, (void *)*helloworld_one, "helloworld");
pthread_create(&thread_id_two, NULL, (void *)*helloworld_two, "helloworld");
pthread_join(thread_id_one, NULL);
pthread_join(thread_id_two, NULL);
}

两个线程打印的都是修改后的值

使用TSD,值不同

#include<stdio.h>
#include<pthread.h>
//线程私有数据类型
pthread_key_t key;
void echomsg(void *t)
{
printf("destructor excuted in thread %u, param=%u\n",pthread_self(),((int *)t));
}
void * child1(void *arg)
{
int i = ;
int tid = pthread_self();
printf("\nset key value %d in thread %u\n", i, tid);
//修改私有数据值
pthread_setspecific(key, &i);
//等待让另一个线程修改值
printf("thread one sleep 2 until thread two finish\n");
sleep();
printf("\nthread %u returns %d, add is %u\n", tid, *((int *)pthread_getspecific(key)),
(int *)pthread_getspecific(key));
}
void * child2(void *arg)
{
int temp = ;
int tid = pthread_self();
printf("\nset key value %d in thread %u\n", temp, tid);
//修改私有数据值
pthread_setspecific(key, &temp);
sleep();
printf("\nthread %u returns %d, add is %u\n", tid, *((int *)pthread_getspecific(key)),
(int *)pthread_getspecific(key));
}
int main(void)
{
pthread_t tid1, tid2;
pthread_key_create(&key, echomsg);
pthread_create(&tid1, NULL, (void *)child1, NULL);
pthread_create(&tid2, NULL, (void *)child2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_key_delete(key);
return ; }

【linux高级程序设计】(第十二章)Linux多线程编程的更多相关文章

  1. linux高级管理第十二章--rsync

    实验部分 1.安装rsync 2.配置文件 3.配置密码 4.后续 5.为了测试,创建几个文件 配置实时同步 1.调整inotify内核参数 安装inotify-tools 测试同步 编写脚本 验证 ...

  2. 读书笔记 - js高级程序设计 - 第十二章 DOM2和DOM3

      Node类型的变化   访问元素的样式 myDiv.style.backgroundColor = "red" myDiv.style.width = "100px& ...

  3. 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条

    http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程 ...

  4. 鸟哥的linux私房菜——第十二章学习(Shell Scripts)

    第十二章  Shell Scripts 1.0).什么是shell scripts? script 是"脚本.剧本"的意思.整句话是说, shell script 是针对 shel ...

  5. 读书笔记 - js高级程序设计 - 第十五章 使用Canvas绘图

    读书笔记 - js高级程序设计 - 第十三章 事件   canvas 具备绘图能力的2D上下文 及文本API 很多浏览器对WebGL的3D上下文支持还不够好   有时候即使浏览器支持,操作系统如果缺缺 ...

  6. 鸟哥的Linux私房菜——第十二章:档案的压缩与打包

    视频链接: 土豆:http://www.tudou.com/programs/view/GncwT0FJKsQ B站(推荐):http://www.bilibili.com/video/av98857 ...

  7. 第三十二章 Linux常规练习题(一)

    一.练习题一 1.超级用户(管理员用户)提示符是____,普通用户提示符是____.2.linux关机重启的命令有哪些 ?3.bash是什么?4.bash特性, 常见的bash特性有哪些?5.网卡的配 ...

  8. 第十二章 Linux三剑客之老三—grep

    一.Linux grep 命令用于查找文件里符合条件的字符串. Linux系统中的grep命令是一种功能强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.grep全称是Global ...

  9. 【linux高级程序设计】(第九章)进程间通信-管道 1

    Linux操作系统所支持的主要进程间的通信机制. 无名管道 PIPE cat test.txt| grep hello 上面这种管道,将一个命令的输出作为另一个命令的输入,而这种管道是临时的,命令执行 ...

  10. 第十二章Linux文件系统与日志

    1.inode 包含文件的元信息(1)inode 内容:文件的字节数.拥有者的 UID.GID.文件的读写执行权限.时间戳等,但不包含文件名.文件名是储存在目录的目录项中.(2)查看文件的 inode ...

随机推荐

  1. PHP.TP框架下商品项目的优化4-优化商品添加表单js

    优化商品添加表单js 思路 1.制作五个按钮 2.下面五个table 3.全部隐藏,点击则显示 4.点击第几个按钮就显示第几个table 具体操作 1.添加按钮 2.添加五个table并添加class ...

  2. ElasticSearch学习笔记(三)-- 查询

    1. URISearch详解与演示 2. QueryDSL简介 3. 字段类查询简介及match-query 4. 相关性算分 5. match-phrase-query 6. query-strin ...

  3. 哪些工具能有效管理Azure Active Directory?

    [TechTarget中国原创] 管理Azure Active Directory有四种常见的工具:Azure Web门户.Azure PowerShell.Azure命令行接口和Azure Mana ...

  4. runtime怎么添加属性、方法等

    ivar表示成员变量 class_addIvar class_addMethod class_addProperty class_addProtocol class_replaceProperty

  5. 《Cracking the Coding Interview》——第17章:普通题——题目5

    2014-04-28 22:44 题目:猜数字游戏.四个数字,每个都是0~9之间.你每猜一次,我都告诉你,有多少个位置和数字都对(全对),有多少个位置错数字对(半对).比如“6309”,你猜“3701 ...

  6. 【LoadRunner】LR编写Dubbo协议脚本

    一.Dubbo服务简介 Dubbo是一个分布式服务架构,把核心业务抽取出来作为独立的服务,使前端应用能更快速和稳定的响应. Dubbo服务工作原理:服务提供方提供接口,并提供接口的实现,提供方注册服务 ...

  7. 小红帽安装centos的yum的一些坑!

    [root@localhost ~]# lsanaconda-ks.cfg yum-3.4.3-158.el7.centos.noarch.rpm yum-updateonboot-1.1.31-45 ...

  8. Python全栈工程师(for、列表)

    ParisGabriel     Python 入门基础         for:用来遍历可迭代对象的数据元素可迭代对象是指以此获取数据元素的对象可迭代对象包括:字符串 str 列表 list元组 t ...

  9. TensorFlow dataset API 使用

    # TensorFlow dataset API 使用 由于本人感兴趣的是自然语言处理,所以下面有关dataset API 的使用偏向于变长数据的处理. 1. 从迭代器中引入数据 import num ...

  10. 团队项目-第九次scrum 会议

    时间:11.5 时长:40分钟 地点:F楼1039教室 工作情况 团队成员 已完成任务 待完成任务 解小锐 完成员工commit函数的数值函数编写 完成多种招聘方式的逻辑编写 陈鑫 实现游戏的暂停功能 ...