您的位置:首页 > 运维架构 > Linux

linux线程

2016-04-13 22:48 351 查看

现代操作系统特定引入“进程”的概念。

1. 进程地址空间是独立的,

创建一个进程的系统开销比较大,要拷贝整个地址空间,

进程切换开销比较大;

2. 进程间的数据是独立的,分开的,如果进程间需要进行

数据交换,则需要用到进程间通信(IPC)

进程通信开销比较大。

于是,就有人提出能不能在同一个进程内实现“任务(程序,代码)”的

并发执行呢?



线程概念

1. 线程是比进程更小的活动单位,它是进程中的一个执行路径(执行分支)

2. 线程同进程内其他线程共享进程地址空间;

=>

线程特点:

1. 创建一个线程比创建一个进程开销小得多

在进程内部创建一个线程,不需要拷贝进程的地址空间,

新线程和其他的线程共享进程的地址空间。

而进程与进程间地址空间是独立的,创建时需要拷贝整个进程地址空间。

2. 实现线程间通信十分方便,因为一个进程创建的多个线程直接共享整个进程的

内存区域。

3. 线程也是一个动态概念。

4. 进程是操作系统资源分配的最小单位,线程是调度的最小单位。

5. 在进程内创建多线程,可以提高系统的并行处理能力,加快进程的处理速度。

6. 每个进程会自动有一个主线程,就是main函数, 这个主线程如果执行完了

那么进程就结束了。

“并发粒度不一样”

POSIX线程实现接口:(POSIX--Portable Operation System Interface)

POSIX实现了线程,我们叫POSIX thread, pthread

linux中pthread的接口函数:

sudo apt-get install manpages-posix-dev 用于安装pthread相关的manual手册(因为man中查不到pthread_mutex_init,pthread_mutex_destory等函数)

1.创建一个线程

2.线程退出

3.线程资源回收

4. 线程间同步机制

(1) 互斥

(2) 条件变量

1.创建一个线程

pthread_create

NAME

pthread_create - create a new thread

线程用来并发执行一个任务,"任务"就是代码,

在C语言,代码(指令)是以函数形式组织。

在pthread中,每个线程都有一个唯一的ID,用类型pthread_t来表示。

而且我们可以通过函数:pthread_t pthread_self(void);来获取自身线程的ID

而且每个线程都有自己的属性,用类型pthread_attr_t来表示

SYNOPSIS

#include <pthread.h>

int pthread_create(pthread_t *thread,

const pthread_attr_t *attr,

void *(*start_routine) (void *),

void *arg);

thread: 指向pthread_t,用来保存新创建的线程的ID的

attr:指向线程属性结构体pthread_attr_t, 一般为NULL,

表示采用默认的属性。

start_routine: 函数指针。指向的函数的类型为返回一个

void*,带一个void*参数,start_routine指向的函数,就是

线程函数,新创建的线程要执行的任务。

arg: 该参数,将作为线程函数的实际参数传给线程函数

返回值:

成功返回0,

失败返回-1, errno被设置。

Compile and link with -pthread.编译时需要指定,如:gcc main.c -o main -l pthread

2.线程退出

三种方式可以使线程结束:

(1) 线程函数返回,任务完成。

(2)在线程函数中调用pthread_exit()

(3) It is cancelled (别的线程调用pthread_cancell,让线程"取消了")

2.2 pthread_exit

NAME

pthread_exit - terminate calling thread

SYNOPSIS

#include <pthread.h>

pthread_exit用来结束调用线程,

void pthread_exit(void *retval);

retval:返回一个指针。

Compile and link with -pthread.

2.3

NAME

pthread_cancel - send a cancellation request to a thread

SYNOPSIS

#include <pthread.h>

pthread_cancel用来发送一个"取消"请求给thread指定的线程,

但是接收到"取消"请求的线程,不一定会成功取消并退出

这得取决于,接收线程的属性(cancel state)

我们可以调用pthread_setcancelstate来 enable/disable线程这个属性(

可"取消"属性,cancel state)

int pthread_cancel(pthread_t thread)

SYNOPSIS

#include <pthread.h>

int pthread_setcancelstate(int state, int *oldstate);

state: 要设置的取消状态

PTHREAD_CANCEL_ENABLE :可被别人取消

PTHREAD_CANCEL_DISABLE: 不可被别人取消

oldstate:保存上次的状态值

返回值:

成功返回0,

失败返回-1, errno被设置

3.线程资源回收

pthread_join等待一个指定的线程退出,该函数会阻塞调用线程,

直到被等待的线程退出,它有两个作用:

(1) 等待线程退出;

(2) 回收被等待线程的资源的

线程退出了,不代表其资源释放完全了,(这个时候需要调用

pthread_join去回收其资源),

线程退出了是否代表其资料完全释放,这个得取决于一个

线程属性: detach state(分离属性)

PTHREAD_CREATE_DETACHED -> detach state : 分离状态,线程结束,资源就自动释放

PTHREAD_CREATE_JOINABLE -> joinable state : joinable state , 这种状态,就必须要另外线程调用

pthread_join来回收其资源。

我们可以调用pthread提供的API来设置线程的detach state:

NAME

pthread_detach - detach a thread

SYNOPSIS

#include <pthread.h>

int pthread_detach(pthread_t thread);

Compile and link with -pthread.

DESCRIPTION

pthread_detach这个函数标记thread代表的那个线程

为分离状态。

当一个处于分离状态的线程退出时,它的资源

会自动回收给系统,没有必要让另外的线程手动

回收它的资源。

试图去分离一个已经处于分离状态的线程将导致不确定

的情况。

NAME

pthread_join - join with a terminated thread

SYNOPSIS

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

thread:要等待哪个线程退出

retval:二级指针,用来保存退出线程的返回值的。

此处为什么一定要用二级指针呢? 因为一个线程的返回值是void*类型

返回值:

成功返回0,

失败返回-1, errno被设置

4. 线程间同步机制

(1) 互斥

多个线程同时访问一个共享资源时,我们必须要

"避免竞争"

a. 信号量机制

SYSTEM V 信号量

POSIX信号量(有名/无名)

b. 线程互斥锁(pthread mutex)

线程互斥锁专门用来实现在线程间互斥用的,

它的原理和功能,作用等都和信号量一样。

pthread_mutex_t这种类型就是用来表示线程互斥锁的。

pthread mutex的操作:

(1) 初始化一个互斥锁; 销毁一个互斥锁

pthread_mutex_init用特定的线程互斥锁属性(pthread_mutexattr_t)

来初始化mutex

int pthread_mutex_init(pthread_mutex_t *restrict mutex,

const pthread_mutexattr_t *restrict attr);

mutex:指向要初始化的线程互斥锁

attr:指向线程互斥锁的属性,一般为NULL,

表示采用默认属性。

返回值:

成功返回0

失败返回-1, errno被设置

pthread_mutex_destroy用来销毁mutex指向的线程互斥锁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

(2) lock; //P操作

pthread_mutex_lock用来对互斥锁进行P操作的

会阻塞到获取该互斥锁,或出错。

返回0表示,获取了互换锁

返回-1 表示出错了,errno被设置

int pthread_mutex_lock(pthread_mutex_t *mutex); //block

pthread_mutex_trylock用来对互斥锁进行P操作,只不过

它是非阻塞的版本,能获取则获取,不能获取就返回。

返回0表示,获取了互斥锁

返回-1表示没获取到,(可能是出错了,errno)

int pthread_mutex_trylock(pthread_mutex_t *mutex);

(3) unlock; //V操作

线程互斥锁的V操作

int pthread_mutex_unlock(pthread_mutex_t *mutex);

(2) 条件变量

(2.1)初始化/销毁一个条件变量

pthread_cond_init用来初始化cond指向条件变量

int pthread_cond_init(pthread_cond_t * cond,

const pthread_condattr_t * attr);

cond: 指向要初始化的条件变量

attr:指向条件变量的属性,一般为NULL,表示采用默认属性

返回值:

成功返回0

失败返回-1, errno被设置

pthread_cond_destroy用来销毁cond指向的条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

(2.2)等待一个条件变量

条件变量本身就是一个共享对象(多个线程都可以访问它),

它本身也需要保护。MC(Mutex Condition)

//死等,直到被唤醒(返回0, 表示条件产生,

返回-1,表示失败了)

int pthread_cond_wait(pthread_cond_t *restrict cond,

pthread_mutex_t *restrict mutex);

//有限等待。

//返回0表示条件产生,

//返回-1 表示失败,errno

int pthread_cond_timedwait(pthread_cond_t *restrict cond,

pthread_mutex_t *restrict mutex,

const struct timespec *restrict abstime);

struct timespec {

time_t tv_sec; // Seconds

long tv_nsec; // Nanoseconds [0 .. 999999999]

};

例子:

struct timespce ts;

clock_getime(CLOCK_REALTIME, &ts);

//ts.tv_sec +=5;

//ts.tv_nsec

ts.tv_nsec += 1000000;

if (ts.tv_nsec >=1000000000L)

{

ts.tv_sec++;

ts.tv_nsec -= 1000000000L;

}

NOTE:调用上述函数时,需要把锁住的互斥锁传入,上述等待函数

在让线程休眠(让出CPU)前,会释放该互斥锁,然后阻塞,直到被

唤醒,再次锁住该互斥锁,并从等待函数中返回。

(2.3)唤醒一个条件变量

int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒条件变量上等待的所有线程

int pthread_cond_signal(pthread_cond_t *cond); //唤醒条件变量上等待的一个线程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: