目录
一 为什么使用线程
1.1概念
1.2使用线程的理由
二 线程的创建,退出,等待
2.1 线程创建
2.2 线程退出
2.3.线程等待
2.4. 线程ID获取及比较
一 为什么使用线程
1.1概念
概念:"进程——资源分配的最小单位,线程——程序执行的最小单位"。线程是进程内部的一条执行序列或执行路径,一个进程可以包含多条线程。线程是进行资源调度和分配的基本单位 。
一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。
- 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。
- 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程包含了表示进程内执行环境必须的信息,其中包括进程中表示线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno常量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
- 在Unix和类Unix操作系统中线程也被称为轻量级进程(lightweight processes),但轻量级进程更多指的是内核线程(kernel thread),而把用户线程(user thread)称为线程。
1.2使用线程的理由
多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
- 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
- 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
- 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
二 线程的创建,退出,等待
2.1 线程创建
函数原型:pthread_create()
#include <pthread.h>
// 返回:若成功返回0,否则返回错误编号
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_rtn)(void *), void *restrict arg);
首先,使用该函数需要包含头文件。
函数参数及返回值如下所示:
- thread: 由tidp指向的内存单元被设置为新创建线程的线程ID
- attr: attr用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。
- start_rtn(): 新创建的线程从start_rtn函数的地址开始运行,
- argv: 该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
- 返回值:若成功返回0,否则返回错误编号
当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void fun1()
{
while(1)
{
puts("线程");
sleep(1);
}
}
void main()
{
pthread_t t1;
pthread_create(&t1,NULL,(void *)fun1,NULL);
while(1)
{
puts("主线程");
sleep(1);
}
}
运行结果,主线程和创建的新线程每秒打印一次字符串
2.2 线程退出
单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:
- 线程只是从启动例程中返回,返回值是线程的退出码。
- 线程可以被同一进程中的其他线程取消。
- 线程调用pthread_exit:
#include <pthread.h>
int pthread_exit(void *rval_ptr);
首先,使用该函数需要包含头文件。
函数参数及返回值如下所示:
retval 是 void* 类型的指针,可以指向任何类型的数据,它指向的数据将作为线程退出时的返回值。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。
retval是返回信息,”临终遗言“,可以给可以不给
该变量不能使用临时变量。
可使用:全局变量、堆上开辟的空间、字符串常量
2.3.线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);// 返回:若成功返回0,否则返回错误编号
函数参数及返回值如下所示:
- thread: 要等待的线程标识符
- retval: 接收返回值:若是return退出的,接收入口函数的返回值,pthread_exit退出的,接收该函数的参数
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void fun1()
{
while(1)
{
puts("线程");
sleep(1);
pthread_exit(NULL);
}
}
void main()
{
pthread_t t1;
pthread_create(&t1,NULL,(void *)fun1,NULL);
pthread_join(&t1,NULL);
}
调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果例程只是从它的启动例程返回,rval_ptr将包含返回码。
如果对线程的返回值不感兴趣,可以把rval_ptr置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获得线程的终止状态。
2.4. 线程ID获取及比较
线程ID获取 pthread_self()
#include <pthread.h>
pthread_t pthread_self(void);// 返回:调用线程的ID
对于线程ID比较,为了可移植操作,我们不能简单地把线程ID当作整数来处理,因为不同系统对线程ID的定义可能不一样。我们应该要用下边的函数:
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
// 返回:若相等则返回非0值,否则返回0
返回值:若相等则返回非0值,否则返回0