线程与进程对比

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. CSS计数器(自定义列表)Demo

    html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <ti ...

  2. CodeForces 873F Forbidden Indices 后缀数组

    忘了当时怎么做的了,先把代码贴上,保存一下后缀数组模板. #include <cstdio> #include <cstring> #include <algorithm ...

  3. anr trace文件分析

    测试给的trace文件好几万行,怎么看? 1.搜索 你的包名,看它报错误报在你代码的哪里 2.在你代码里面分析 还有,synchronized 就是用来防止多线程调用的,没有那么神奇.

  4. Thread-local storage (TLS)

    线程存储原理:为变量在每一个现存的线程里分配一个实例,需要处理器支持,并不是所有都支持!支持全局的,静态的变量,但不支持局部变量. 关键字 __thread   __thread int i;   e ...

  5. laravel5.5中间件

    目录 1. 中间件知识 1. artisan 命令 2. 文件内容 3. 前置中间件和后置中间件 4. 使用中间件 2. 控制器中间件 1. 中间件知识 1. artisan 命令 php artis ...

  6. PJMEDIA之录音器的使用(capture sound to avi file)

    为了熟悉pjmedia的相关函数以及使用方法,这里练习了官网上的一个录音器的例子. 核心函数: pj_status_t pjmedia_wav_writer_port_create ( pj_pool ...

  7. Entity Framework(三)---FluentAPI和增删查改

    一.FluentAPI: 1.基本配置: namespace ConsoleApp14.ModelConfig { public class PersonConfig: EntityTypeConfi ...

  8. A. Vasya and Book

    题目原址 http://codeforces.com/contest/1082/problem/A 题目内容 一共n页书,现在位于第x位,想要看第y页,每次只能翻d页,注意总能翻到第1页和第n页. V ...

  9. Gluon

    推荐一门mxnet的学习框架gluon 首先是学习网址链接gluon 基本环节分成两部分 从0开始(介绍不使用框架,而只使用mxnet来完成神经网络的搭建) gluon实现(介绍使用框架快速搭建神经网 ...

  10. Java 复习计划

    前言 打算下学期开学,也就是九月份,去找实习,现在还有三个月时间.(然而还在天天玩 Python..) 定个复习计划. 1. 基础 并发:Java并发编程实战 [ x ] SQL:MySQL,看看书, ...