【linux高级程序设计】(第十二章)Linux多线程编程

线程与进程对比

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(1);
}

int main(int argc, char *argv[])
{
    struct message test;
    pthread_t thread_id;
    test.i = 10;
    test.j = 20;
    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 0;
}

void *helloworld(char *argc)
{
    int *p;
    p = (int *)malloc(10 * sizeof(int));
    printf("the message is %s\n", argc);
    printf("the child id is %u\n", pthread_self());
    memset(p, ‘c‘, 10);
    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(1)
    {
        printf("test message\n");
        sleep(1);
    }
    pthread_cleanup_pop(1);
}
int main()
{
    pthread_t tid;
    //创建线程
    pthread_create(&tid, NULL, (void *)test_cancel, NULL);
    sleep(2);
    //取消子线程
    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 != 0)
    {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    sleep(3);
    printf("Cancelling thread...\n");
    //取消子线程
    res = pthread_cancel(a_thread);
    if(res != 0)
    {
        perror("Thread cancelation failed");
        exit(EXIT_FAILURE);
    }
    printf("Waitint for thread to finish...\n");
    //等待子线程结束
    res = pthread_join(a_thread, &thread_result);
    if(res != 0)
    {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

//新线程执行函数
void *thread_function(void *arg)
{
    int i, res, j;
    sleep(1);
    //设置为不可取消
    res = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    if(res != 0)
    {
        perror("Thread pthread_setcancelstate failed");
        exit(EXIT_FAILURE);
    }
    sleep(3);
    printf("thread cancle type is disable, can‘t cancle this thread\n");
    for(i = 0; i < 3; i++)
    {
        printf("Thread is running (%d)...\n", i);
        sleep(1);
    }
    //设置为可取消
    res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    if(res != 0)
    {
        perror("Thread pthread_setcancelstate failed");
        exit(EXIT_FAILURE);
    }
    printf("Now change the canclestate is ENABLE\n");
    sleep(200);
    pthread_exit(0);
}

线程与私有数据

如果需要每个线程有自己私有的全局变量,可以使用同名而不同内存地址的线程私有数据结构,成为私有数据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 = 100;
void *helloworld_one(char * argc)
{
    printf("the message is %s\n", argc);
    //修改值为10
    key = 10;
    printf("key=%d, the child id is %u\n", key, pthread_self());
    return 0;
}
void *helloworld_two(char * argc)
{
    printf("the message is %s\n", argc);
    //休眠,让另一个线程先修改值
    sleep(1);
    printf("key=%d, the child id is %u\n", key, pthread_self());
    return 0;
}
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 = 10;
    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(2);
    printf("\nthread %u returns %d, add is %u\n", tid, *((int *)pthread_getspecific(key)),
    (int *)pthread_getspecific(key));
}
void * child2(void *arg)
{
    int temp = 20;
    int tid = pthread_self();
    printf("\nset key value %d in thread %u\n", temp, tid);
    //修改私有数据值
    pthread_setspecific(key, &temp);
    sleep(1);
    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 0;

}

时间: 2024-11-03 20:44:02

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

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

Node类型的变化   访问元素的样式 myDiv.style.backgroundColor = "red" myDiv.style.width = "100px" 计算的样式 记住所有计算的样式都是只读的 偏移量 offsetHeight 外边框外 offsetWidth offsetLeft  外边框外 到 左端 offsetTop  客户区的大小 clientWidth  内边框外缘 clientHeight 内边框外缘 滚动大小 scrollHeight

Linux与云计算——第二阶段Linux服务器架设 第一十二章:数据库搭建—PostgreSQL

Linux与云计算--第二阶段Linux服务器架设 第一十二章:数据库搭建-PostgreSQL 1.1 安装PostgreSQL [1] 安装并启动PostgreSQL. [[email protected] ~]# yum -y install postgresql-server [[email protected] ~]# postgresql-setup initdb Initializing database ... OK [[email protected] ~]# vim /var

第二十二章 Linux文件比较,文本文件的交集、差集与求差:comm命令

第二十二章 Linux文件比较,文本文件的交集.差集与求差:comm命令 名词解释 comm 命令 可以用于两个文件之间的比较,它有一些选项可以用来调整输出,以便执行交集.求差.差集操作. 交集:打印两个文件所共有的行 求差:打印出指定文件所包含的其不相同的行. 差集:打印出包含在一个文件中,但不包含在其他指定文件中的行. 语法 comm(选项)(参数) 选项 -1 :不显示在第一个文件出现的内容: -2 :不显示在第二个文件中出现的内容: -3 :不显示同时在两个文件中都出现的内容. ? 参数

javascript高级程序设计 第十四章--表单脚本

javascript高级程序设计 第十四章--表单脚本 在HTML中表单由<form>元素表示,在js中表单对应的是HTMLFormElement类型,这个类型也有很多属性和方法:取得表单元素的引用还是为它添加id特性,用DOM操作来获取表单元素:提交表单:把<input>或<button>元素的type特性设置为"submit",图像按钮把<input>元素的type特性设置为"image",也可以调用submit(

Java学习笔记—第十二章 Java网络编程入门

第十二章  Java网络编程入门 Java提供的三大类网络功能: (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置.利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端. (2)Socket:又称"套接字",用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务).在客户/服务器网络中,当客

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

http://blog.csdn.net/terryzero/article/details/3797782 疯狂JAVA讲义---第十二章:Swing编程(五)进度条和滑动条 标签: swing编程java任务timerstring 2009-01-16 21:12 6722人阅读 评论(0) 收藏 举报  分类: J2SE(63)  版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天讲了Swing基本的控件,今天开始讲特殊控件.一天讲2个吧,首先讲用JProgressBar,Pro

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

读书笔记 - js高级程序设计 - 第十三章 事件 canvas 具备绘图能力的2D上下文 及文本API 很多浏览器对WebGL的3D上下文支持还不够好 有时候即使浏览器支持,操作系统如果缺缺乏必要的绘图驱动程序,则浏览器即使支持了也没用   <canvas> var drawing = document.getElementById("drawing"); if( drawing.getContext ){ drawing.getContext("2d"

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

视频链接: 土豆: B站(推荐): 本章目录: 1. 压缩档案的用途与技术: (为啥要压缩嘞?因为比如存储一个数字1,前7位补充0浪费,压缩技术就是把空的那些用上,省硬盘空间,下载的时候也省带宽)2. Linux 系统常见的压缩指令:2.1 compress (这个是最老的压缩指令,现在不怎么用了)2.2 gzip, zcat (新一代的压缩指令,代替了compress,zcat是读取gzip和compress的压缩数据的指令)2.3 bzip2, bzcat   (bzip2更高效,压缩比高,

第十二章 linux磁盘管理-lvm(2)

yum install lvm2-2.02.72-8.el6_0.4.x86_64 1.将新创建的两个分区/dev/sdb1 /dev/sdb2转化成物理卷,主要是添加LVM属性信息并划分PE存储单元. pvcreate /dev/sdb1 /dev/sdb2 pvs pvdisplay 2.创建卷组 vgdata ,并将刚才创建好的两个物理卷加入该卷组.可以看出默认PE大小为4MB,PE是卷组的最小存储单元.可以通过 –s参数修改大小 vgcreate vgdata /dev/sdb1 /de

第十二章 &nbsp; Linux &nbsp; DNS域名解析

实验要求: n 主域名服务器地址为ns1.benet.com (173.16.16.5) n 从域名服务器地址为ns2.benet.com(173.16.16.6) n 为benet.com.accp.com区域提供以下解析记录. l Mail.benet.com(123.45.67.89).ftp.benet.com(123.45.67.90) l www.benet.com(123.45.67.88).*.benet.com(123.45.67.88) l www.accp.com (58.