UNIX环境高级编程-第11章- 线程 - 一

11.2 线程的基本概念

        线程是一个进程中的控制和执行单元,也是 CPU 调度和分派的基本单元。在传统的 UNIX 系统中,一个进程只有一个线程(主线程),即在同一时刻只能做一件事情。在现代的 UNIX 系统中,一个进程可以包含多个线程,每个线程处理各自独立的任务,称之为并发(concurrency,但并不等同于并行(parallelism”。真正意义上的并行只存在于多处理器系统中,而并发也可以存在于单处理器中。并行要求程序能够同时执行多个操作,而并发只要求程序能够假装同时执行多个操作,处理器的数量并不影响程序结构。

        进程是系统中程序执行和资源分配的基本单位,每个进程有自己的数据段、代码段和堆栈段。在进程中可以创建多个线程,并发地完成多个不同的任务。线程各自有独自的线程ID、寄存器值、栈、调度优先级和策略、信号屏蔽字、errno 变量以及线程私有数据,而进程的可执行的程序文本、全局内存和堆内存、栈以及文件描述符对该进程的所有线程都是共享的。线程与进程之间的关系可由下图表示:


 多线程优点:

(1)通过为每种事件类型的处理分配单独的线程,能够简化处理异步事件的代码。每个线程在进行事件处理时可以采用同步编程模式,同步编程模式要比异步编程模式简单得多。

(2)多个进程必须使用操作系统提供的复杂机制才能实现内存和文件描述符的共享,而多个线程自动地可以访问相同的存储地址空间和文件描述符

(3)有些问题可以通过将其分解从而改善整个程序的吞吐量。在单进程的情况下,多个任务只能串行完成;如果有多个线程,相互独立的任务的处理就可以交叉进行。

(4)交互的程序同样可以通过使用多线程实现响应时间的改善,多线程可以把程序中处理用户输入输出的部分与其他部分分开。

多线程缺点:

(1)如果单个资源要在多个线程中共享,就必须处理一致性问题,也就是要进行相应的同步操作。如果使用过多的同步就很容易损失性能。

(2)为了编写能够在多个线程中正确工作的代码就必须认真的思考和计划,不得不考虑死锁、竞争、优先级倒置等问题。

(3)调试异步代码也比同步代码困难得多。调试不可避免地要改变事件的时序,如果某个线程因调试陷阱而运行的稍微慢了些,则你要跟踪的问题可能不会再现。跟踪内存错误将更加困难。

11.3线程标识

每个线程都有一个线程ID,线程ID只在它所属的进程环境中有效。线程ID使用 pthread_t 数据类型来表示,实现的时候可以使用一个结构来表示pthread_t 数据类型,所以可移植的操作系统实现不能将它比作整数处理,因此必须使用 pthread_equal 函数来对比线程ID进行比较。 线程可以通过调用pthread_self 函数获得自身线程ID。

/* 线程 */  
  
/* 
 * 函数功能:比较两个线程ID; 
 * 返回值:若相等则返回非0,否则返回0; 
 * 函数原型: 
 */  
#include <pthread.h>  
  
int pthread_equal(pthread_t tid1, pthread_t tid2);  
  
/* 
 * 函数功能:获取自身的线程ID; 
 * 返回值:调用线程的线程ID; 
 * 函数原型: 
 */  
pthread_t pthread_self(void);  

11.4 线程的创建

线程可以通过调用 pthread_create 函数创建。线程创建时并不能保证哪个线程会先运行,新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。在当前线程从函数pthread_create 中返回以及新线程被调度执行之间不存在同步关系,即新线程可能在当前线程从pthread_create 返回之前就运行了。甚至在 pthread_create 返回之前,新线程就可能已经运行完毕了。

/* 
 * 函数功能:创建线程; 
 * 返回值:若成功则返回0,否则返回错误编号; 
 * 函数原型: 
 */  
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr,  
                void *(*start_rtn)(void *), void *arg);  
/* 
 * 说明: 
 * 当该函数成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID; 
 * attr参数用于定制各种不同的线程属性,当为NULL表示创建默认属性的线程; 
 * 新创建的线程从start_rtn函数的地址开始运行,该函数只有一个指针参数arg, 
 * 如果需要向start_rtn函数传递的参数不止一个,则需要把参数放在一个结构中, 
 * 然后把这个结构的地址作为arg参数传入; 
 */  

测试程序:

#include "apue.h"  
#include <pthread.h>  
  
pthread_t ntid;  
  
static void printids(const char *);  
static void *func(void*);  
  
int main(void)  
{  
    int err;  
  
    err = pthread_create(&ntid, NULL, func, NULL);  
    if(err != 0)  
        err_quit("can't create thread: %s\n",strerror(err));  
    printids("main thread: ");  //主线程调用printids
    sleep(1);  //如果主线程不休眠,它就可能退出,这样在新线程有机会运行之前,整个进程可能就已经终止了。这种行为特征依赖于操作系统中的线程实现和调度算法。
    exit(0);  
}  
  
static void printids(const char *s)  
{  
    pid_t pid;  
    pthread_t tid;  
  
    pid = getpid();  
    tid = pthread_self();  
  
    printf("%s pid %u tid %u (0x%x)\n",s,(unsigned int)pid,(unsigned int)tid,(unsigned int)tid);  
}  
  
static void *func(void *arg)  
{  
    printids("new thread: ");  //新线程调用printids
    return((void*)0);  
}  

输出结果:

[root@localhost 11]# gcc 11-1.c -lpthread
[root@localhost 11]# ./a.out
main thread:  pid 5362 tid 3086317248 (0xb7f576c0)
new thread: pid 5362 tid 3086314384 (0xb7f56b90)

        该程序实现的功能是在当前进程新建线程,并打印出主线程和新建线程的线程ID以及进程的ID,从结果可以知道,这两个线程在同一个进程中; 注意:在  Linux 下编译线程程序是必须加上 -lpthread  参数,因为  pthread  库不是 Linux  系统默认的库,连接时需要使用库 libpthread.a

当把  sleep(1);注释掉后的输出结果为:

[root@localhost 11]# gcc 11-1.c -lpthread
[root@localhost 11]# ./a.out
main thread:  pid 20441 tid 3086051008 (0xb7f166c0)
[root@localhost 11]#


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值