C再学习 10 - 线程

C再学习 10 - 线程

看到前边多进程的时候就知道肯定后边有多线程,否则只用进程就太重型了。但是使用线程就要碰到数据共享的问题了。 创建线程 C的线程库是pthread.h,使用这个库来操作线程。 使用线程的步骤是: 创建需要在线程中运行的函数 创建一个结构pthread_t pthread_create()创建并立刻运行

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

创建线程

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类型的转换上。 系统编程的时候再来看吧。
LICENSED UNDER CC BY-NC-SA 4.0
Comment