看到前边多进程的时候就知道肯定后边有多线程,否则只用进程就太重型了。但是使用线程就要碰到数据共享的问题了。

创建线程

C的线程库是pthread.h,使用这个库来操作线程。

使用线程的步骤是:

  1. 创建需要在线程中运行的函数
  2. 创建一个结构pthread_t
  3. pthread_create()创建并立刻运行线程
  4. 主线程需要监听线程,不能立刻结束,否则尚未运行完的线程也没了

由于线程库不是C标准库,需要在使用的时候使用参数-lpthread链接该库。

来创建两个在线程里执行的函数,注意,这种函数的返回值必须是 void* ,也就是通用指针类型:

void *overtime(void *a) {
    int i = 0;

    for (; i < 10; i++) {
        sleep(1);
        puts("still overtime .....");
    }

    return NULL;
}

void *update_git(void *b) {
    int j;
    for (j = 0; j < 10; j++) {
        sleep(1);
        puts("don't forget to learn coding...");
    }
    return NULL;
}

然后创建结构pthread_t并运行:

pthread_t thread0;
pthread_t thread1;

//线程需要运行的函数传递给pthread_create
if (pthread_create(&thread0, NULL, overtime, NULL) == -1) {
    error("can't create thread0");
}
if (pthread_create(&thread1, NULL, update_git, NULL) == -1) {
    error("can't create thread1");
}

之后监听线程是否结束。

//用一个变量接收线程函数返回的指针
void *result;

//pthread_join通过监听线程返回值的方式等待线程
if (pthread_join(thread0, &result) == -1)
    error("无法回收线程t0");
if (pthread_join(thread1, &result) == -1)
    error("无法收回线程t1");

用一个变量接收线程返回的指针,然后针对线程调用函数pthread_join。可以同时监听多个线程。这样主线程会在监听函数执行完之后才继续往下执行,这里就会结束。

完整的main函数如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

int main(int argc, char* argv[])
{
    pthread_t thread0;
    pthread_t thread1;

    //线程需要运行的函数传递给pthread_create
    if (pthread_create(&thread0, NULL, overtime, NULL) == -1) {
        error("can't create thread0");
    }
    if (pthread_create(&thread1, NULL, update_git, NULL) == -1) {
        error("can't create thread1");
    }

    //用一个变量接收线程函数返回的指针
    void *result;

    //pthread_join通过监听线程返回值的方式等待线程
    if (pthread_join(thread0, &result) == -1)
        error("无法回收线程t0");
    if (pthread_join(thread1, &result) == -1)
        error("无法收回线程t1");

    return 0;
}

这段代码也只能在linux下编译,需要链接库:

gcc main.c -o threads -lpthread

运行之后,可以看到两个线程的执行是乱序的。这也说明具体线程的执行顺序是不确定的。

老问题:线程间共享数据

线程就不像进程,fork的时候会复制全部的变量,然后彼此独立开来。线程依然在同一个进程内,共享同一个进程内的所有数据。

这也就导致如果不加以控制,线程操作数据的结果无法预料。最简单的不加锁的例子。

奇怪的是自己写了个尝试多线程读取变量的程序,发现运行结果竟然相同。。。

那就看加锁的情况吧:

//先初始化锁为不锁状态
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//在进入锁的地方:
if (pthread_mutex_lock(&mutex) != 0) {
    perror("pthread_mutex_lock");
    exit(EXIT_FAILURE);
}

//解锁
if (pthread_mutex_unlock(&mutex) != 0) {
    perror("pthread_mutex_unlock");
    exit(EXIT_FAILURE);
}

类似于锁的使用还有使用信号量,这个留待以后研究了。

后边的给线程传递一个函数似乎不起作用,在linux下编译运行通不过去,报内存错误。

估计问题主要还是出在指针和long类型的转换上。

系统编程的时候再来看吧。