一、线程标识
     和每个进程都有一个进程ID一样,每个线程也有一个线程ID,线程ID是以pthread_t数据类型来表示的,在Linux中,用无符号长整型表示pthread_t,Solaris 把phread_t数据类型表示为无符号整型,FreeBSD 和Mac OS X 用一个指向pthread结构的指针来表示pthread_t数据类型。
     可以使用pthread_self函数获得自身的线程ID。   
#include <pthread.h>
pthread_t pthread_self(void);
 
二、线程创建
     使用pthread_create函数创建新线程 
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_arrt_t *restrict addr, void *(*start_rtn)(void *), void *restrict arg);
     当pthread_create成功返回后,新创建线程的线程ID会被设置成tidp指向的内存单元,attr参数用于定制各种不同的线程属性,后面再讨论线程属性,现在先把它置为null,创建一个具有默认属性的线程。
     新创建的线程从start_rtn函数开始运行,该函数接收一个无类型指针的参数arg,如果要传给它的参数多于一个,可以把参数放到一个结构中,然后把结构的地址作为arg传入。
     线程新建后会继承调用线程的浮点环境和屏蔽字。
例子:  
#include "apue.h"
#include <pthread.h> pthread_t ntid; void printids(const char *s)
{
pid_t pid;
pthread_t tid; pid = getpid();
tid = pthread_self();
printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid);
} void *thr_fn(void *arg)
{
printids("new thread: ");
return ((void *));
} int main(void)
{
int err; err = pthread_create(&ntid, NULL, thr_fn, NULL);
if (err != )
{
err_exit(err, "can't create thread");
} printids("main thread: ");
sleep();
exit();
}
  这个程序有两个特别的地方:第一,主线程需要休眠,如果主线程不休眠,主线程会退出,新线程并没有机会运行。第二,新线程通过pthread_self(),获得自己的线程ID。
./a.out
main thread: pid tid (0xb75e36c0)
new thread: pid tid (0xb75e2b40)
  虽然Linux线程ID是用无符号长整型来表示的,但它们看起来更像指针。
 
三、线程终止
     如果任意线程调用了exit,_exit,_Exit,整个进程都会终止,这个要注意。
     单个线程可以通过以下三种方式退出,且不终止整个进程。
     1.线程可以简单地从启动例程中返回,返回值是线程的退出码。
     2.线程可以被同一进程中的其他线程取消。
     3.调用pthread_exit
 
     先来看pthread_exit退出的情况。
#include <pthread.h>
void pthread_exit(void *rval_ptr);

ravl_ptr是无类型指针,进程中的其他线程可以通过pthread_join函数获得这个指针。

#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);

调用线程将一直阻塞,直至指定的线程退出,rval_ptr就包含返回码,如果线程被取消,rval_ptr指定的内存单元就设置为PTHREAD_CANCELED.可以通过调用pthread_join自动把线程置于分离状态,如果线程已处于分离状态,pthread_join就会调用失败。

例子:
#include "apue.h"
#include <pthread.h> void *thr_fn1(void *arg)
{
printf("thread 1 returning\n");
return (void *);
} void *thr_fn2(void *arg)
{
printf("thread 2 exiting\n");
pthread_exit((void *));
} int main(void)
{
int err;
pthread_t tid1, tid2;
void *tret; err = pthread_create(&tid1, NULL, thr_fn1, NULL); if (err != )
{
err_exit(err, "can't create thread1");
} err = pthread_create(&tid2, NULL, thr_fn2, NULL); if (err != )
{
err_exit(err, "can't create thread2");
} err = pthread_join(tid1, &tret); if (err != )
{
err_exit(err, "can't join thread1");
} printf("thread1 exit code:%ld\n", (long)tret); err = pthread_join(tid2, &tret); if (err != )
{
err_exit(err, "can't join thread2");
} printf("thread2 exit code:%ld\n", (long)tret); return ;
}
./a.out
thread exiting
thread returning
thread1 exit code:
thread2 exit code:
  也可传递包含复杂消息的结构的地址,不过必须注意,这个结构所使用的内存必须在完成调用后仍是有效的。
      线程也可以调用pthread_cancel函数来请求取消同一进程的其他线程
#include <pthread.h>
int pthread_cancel(pthread_t tid);

听着有点霸道,不过也只是请求而已,线程可以选择忽略这个请求。

     线程可以安排它退出时需要调用的函数,这样的函数是由pthread_cleanup_push注册在栈中的,所以执行顺序与注册时相反。
#include <pthread.h>
void pthread_cleanup_push(void(*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);

当线程执行以下动作时,清理函数rtn由pthread_cleanup_push函数调度

     1.调用pthread_exit时
     2.响应取消请求时
     3.用非零execute参数调用pthread_cleanup_pop时。
例子:
#include "apue.h"
#include <pthread.h> void cleanup(void *arg)
{
printf("cleanup: %s\n", (char *)arg);
} void *thr_fn1(void *arg)
{
printf("thread 1 start\n");
pthread_cleanup_push(cleanup, "thread 1 first handler");
pthread_cleanup_push(cleanup, "thread 1 second handler");
printf("thread 1 push complete\n"); if (arg)
{
return (void *);
} pthread_cleanup_pop();
pthread_cleanup_pop(); return (void *);
} void *thr_fn2(void *arg)
{
printf("thread 2 start\n");
pthread_cleanup_push(cleanup, "thread 2 first handler");
pthread_cleanup_push(cleanup, "thread 2 second handler");
printf("thread 2 push complete\n"); if (arg)
{
pthread_exit((void *));
} pthread_cleanup_pop();
pthread_cleanup_pop(); return (void *);
} int main(void)
{
int err;
pthread_t tid1, tid2;
void *tret; err = pthread_create(&tid1, NULL, thr_fn1, (void *));
if (err != )
{
err_exit(err, "can't create thread 1");
} err = pthread_create(&tid2, NULL, thr_fn2, (void *));
if (err != )
{
err_exit(err, "can't create thread 2");
} err = pthread_join(tid1, &tret);
if (err != )
{
err_exit(err, "can't join with thread 1");
}
printf("thread 1 exit code %ld\n", (long)tret); err = pthread_join(tid2, &tret);
if (err != )
{
err_exit(err, "can't join with thread 2");
}
printf("thread 2 exit code %ld\n", (long)tret); return ;
}

 

./a.out
thread start
thread push complete
cleanup: thread second handler
cleanup: thread first handler
thread start
thread push complete
thread exit code
thread exit code
     可知如果线程是通过它的启动例程中返回而终止的话,它的清理处理程序就不会被调用。
 
     在默认情况下,线程的终止状态会一直保存到对该线程调用pthread_join,如果该线程已经被分离,则底层的资源可以在线程终止时立即被收回,不用再调用pthread_join,且再调用pthread_join会出错。
#include <pthread.h>
int pthread_detach(pthread_t tid);

APUE学习之多线程编程(一):线程的创建和销毁的更多相关文章

  1. APUE学习之多线程编程(三):线程属性、同步属性

    一.线程属性      可以使用pthread_attr_t结构修改线程默认属性,并这些属性和创建的线程练习起来,可以使用pthread_att_init函数初始化pthread_attr_t结构,调 ...

  2. APUE学习之多线程编程(二):线程同步

         为了保证临界资源的安全性和可靠性,线程不得不使用锁,同一时间只允许一个或几个线程访问变量.常用的锁有互斥量,读写锁,条件变量           一.互斥量      互斥量是用pthrea ...

  3. .NET面试题解析(07)-多线程编程与线程同步

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实是很多的,比如多线程编程.线程上下文.异步编程.线程同步构造.GUI的跨线程访问等等, ...

  4. .NET面试题解析(07)-多线程编程与线程同步 (转)

    http://www.cnblogs.com/anding/p/5301754.html 系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实 ...

  5. Linux 系统编程 学习:09-线程:线程的创建、回收与取消

    Linux 系统编程 学习:09-线程:线程的创建.回收与取消 背景 我们在此之前完成了 有关进程的学习.从这一讲开始我们学习线程. 完全的开发可以参考:<多线程编程指南> 在Linux ...

  6. vc 基于对话框多线程编程实例——线程之间的通信

     vc基于对话框多线程编程实例——线程之间的通信 实例:

  7. Python中的多线程编程,线程安全与锁(二)

    在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...

  8. Python中的多线程编程,线程安全与锁(一)

    1. 多线程编程与线程安全相关重要概念 在我的上篇博文 聊聊Python中的GIL 中,我们熟悉了几个特别重要的概念:GIL,线程,进程, 线程安全,原子操作. 以下是简单回顾,详细介绍请直接看聊聊P ...

  9. C#多线程编程实例 线程与窗体交互

    C#多线程编程实例 线程与窗体交互 代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = ]; public ...

随机推荐

  1. 【饿了么】招聘Java开发工程师、架构师

    3年以上实际工作经验,本科及以上学历. 具有良好的编程基础( 比如熟悉HTTP.多线程.Socket.JVM.基本的数据结构和算法等). 熟悉Java语言以及相关的服务器(比如Tomcat).工具(M ...

  2. 基本数据结构(2)——算法导论(12)

    1. 引言     这一篇博文主要介绍链表(linked list),指针和对象的实现,以及有根树的表示. 2. 链表(linked list) (1) 链表介绍      我们在上一篇中提过,栈与队 ...

  3. JavaScript权威设计--事件处理介绍(简要学习笔记十七)

    1.事件相关概念 事件类型:一个用来说明发生什么类型事件的字符串 事件目标:是发生的事件或与之相关的对象. 事件处理程序(事件监听程序):是处理货响应事件的函数. 事件对象:是与特定事件相关并且包含有 ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(30)-本地化(多语言)

    系列目录 我们的系统有时要扩展到其他国家,或者地区,需要更多的语言环境,微软提供了一些解决方案,原始我们是用js来控制的,现在不需要了. 我们只要创建简单的资源文件,通过MVC的路由设置就可以轻松的进 ...

  5. Android图片缓存之Lru算法

    前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发 ...

  6. 在Linux Mint上安装node.js和npm

    1.安装Node.js 前端开发过程中,很多项目使用npm的http-server的模块来运行一个静态的服务器,我个人在Dell的笔记本上安装的是Linux Mint最新版本,所以想尝试一下在Linu ...

  7. C#多线程之基础篇2

    在上一篇C#多线程之基础篇1中,我们主要讲述了如何创建线程.中止线程.线程等待以及终止线程的相关知识,在本篇中我们继续讲述有关线程的一些知识. 五.确定线程的状态 在这一节中,我们将讲述如何查看一个线 ...

  8. java进阶之反射:反射基础之如何获取一个类以及如何获取这个类的所有属性和方法(2)

    当我们知道一个类的对象,或者知道一个类的路径,或者指导这个类的名称的时候我们可以获取到这个类的类对象 当我们仅仅知道一个类的类对象的时候我们依然无法操作这个类,因为我们不知道这个类的属性,类的方法.那 ...

  9. geotrellis使用(二十六)实现海量空间数据的搜索处理查看

    目录 前言 前台实现 后台实现 总结 一.前言        看到这个题目有人肯定会说这有什么可写的,最简单的我只要用文件系统一个个查找.打开就可以实现,再高级一点我可以提取出所有数据的元数据,做个元 ...

  10. Linux驱动开发——pr_fmt的用法

    作者:彭东林 邮箱:pengdonglin137@163.com 在阅读kernel代码的时候,总是看到有很多驱动都在第一行定义pr_fmt,闲来没事,分析了一下, 发现,确实挺方便的.下面记录分享一 ...