linux-c之线程

内容纲要

线程概念

线程是允许应用程序并发执行多个任务的一种机制。
由于同一进程的多个线程共享同一地址空间,因
此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如
果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环
境:

  • 文件描述符表
  • 每种信号的处理方式( SIG_IGN 、 SIG_DFL 或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id
    但有些资源是每个线程各有一份的:
  • 线程id
  • 上下文,包括各种寄存器的值、程序计数器和栈指针
  • 栈空间
  • errno 变量
  • 信号屏蔽字
  • 调度优先级
    我们将要学习的线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread。
    在Linux上线程函数位于 libpthread 共享库中,因此在编译时要加上 -lpthread 选项

线程控制

线程的状态:
new-->runnable(on run queue)-->running-->blocked-->running-->dead

创建线程

#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);

返回值:成功返回0,失败返回错误号。
pthread_self()获取当前的线程号

分离线程pthread_detach()

函数原型:int pthread_detach(pthread_t tid);
使用方法:
子线程中加入代码 pthread_detach(pthread_self())
或者父线程调用 pthread_detach(thread_id)(非阻塞,可立即返回)
一旦线程成为可分离线程之后,就不能再使用pthread_join了
可分离线程的使用场景
1、主线程不需要等待子线程
2、主线程不关心子线程的返回码

终止线程

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  • 从线程函数 return 。这种方法对主线程不适用,从 main 函数 return 相当于调用 exit 。
  • 一个线程可以调用 pthread_cancel 终止同一进程中的另一个线程。
  • 线程可以调用 pthread_exit 终止自己
    #include <pthread.h>
    void pthread_exit(void *value_ptr);

    value_ptr 是 void * 类型,和线程函数返回值的用法一样,其它线程可以调用 pthread_join 获得
    这个指针。

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

    调用该函数的线程将挂起等待,直到id为 thread 的线程终止。 thread 线程以不同的方法终止,通
    过 pthread_join 得到的终止状态是不同的,总结如下:

  • 如果 thread 线程通过 return 返回, value_ptr 所指向的单元里存放的是 thread 线程函数的返回值。
  • 如果 thread 线程被别的线程调用 pthread_cancel 异常终止掉, value_ptr 所指向的单元里存放的是常数 PTHREAD_CANCELED 。
  • 如果 thread 线程是自己调用 pthread_exit 终止的, value_ptr 所指向的单元存放的是传
    给 pthread_exit 的参数。
    例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <unistd.h>
    void *thr_fn1(void *arg)
    {
    printf("thread 1 returning\n");
    return (void *)1;
    }
    void *thr_fn2(void *arg)
    {
    printf("thread 2 exiting\n");
    pthread_exit((void *)2);
    }
    void *thr_fn3(void *arg)
    {
    while(1) {
        printf("thread 3 writing\n");
        sleep(1);
    }
    }
    int main(void)
    {
    pthread_t tid;
    void *tret;
    pthread_create(&tid, NULL, thr_fn1, NULL);
    pthread_join(tid, &tret);
    printf("thread 1 exit code %d\n", (int)tret);
    pthread_create(&tid, NULL, thr_fn2, NULL);
    pthread_join(tid, &tret);
    printf("thread 2 exit code %d\n", (int)tret);
    pthread_create(&tid, NULL, thr_fn3, NULL);
    sleep(3);
    pthread_cancel(tid);
    pthread_join(tid, &tret);
    printf("thread 3 exit code %d\n", (int)tret);
    return 0;
    }

线程间同步

mutex(锁)

多个线程同时访问共享数据时可能会冲突,这跟前面讲信号时所说的可重入性是同样的问题。
对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex,Mutual
Exclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有
获得锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要
么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。
Mutex用 pthread_mutex_t 类型的变量表示,可以这样初始化和销毁: