跳到主要内容

线程与线程安全

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

void error(char *msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}

// 定义一个线程函数,它会打印 "Does not!" 五次
void* does_not(void *a)
{
int i = 0;
for (i = 0; i < 5; i++)
{
sleep(1); // 暂停一秒
puts("Does not!"); // 打印消息
}
return NULL; // 返回 NULL
}

// 定义另一个线程函数,它会打印 "Does too!" 五次
void* does_too(void *a)
{
int i = 0;
for (i = 0; i < 5; i++)
{
sleep(1); // 暂停一秒
puts("Does too!"); // 打印消息
}
return NULL; // 返回 NULL
}

int main(void)
{
pthread_t t0; // 定义线程 t0
pthread_t t1; // 定义线程 t1

// 创建线程 t0,执行 does_not 函数
if (pthread_create(&t0, NULL, does_not, NULL) == -1)
error("Can't create thread t0");

// 创建线程 t1,执行 does_too 函数
if (pthread_create(&t1, NULL, does_too, NULL) == -1)
error("Can't create thread t1");

void *result;

// 等待线程 t0 结束
if (pthread_join(t0, &result) == -1)
error("Can't join thread t0");

// 等待线程 t1 结束
if (pthread_join(t1, &result) == -1)
error("Can't join thread t1");

return 0; // 返回 0,表示程序正常结束
}

// 使用 gcc 编译器编译这个程序,需要链接 pthread 库
// gcc thread.c -lpthread -o thread
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>

void error(char *msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}

int beers = 2000000;

pthread_mutex_t beers_lock = PTHREAD_MUTEX_INITIALIZER;
void *drink_lots(void *a)
{
int i;
pthread_mutex_lock(&beers_lock);
for (i = 0; i < 100000; i++)
{
beers = beers - 1;
}
pthread_mutex_unlock(&beers_lock);
printf("beers = %i\n", beers);
return NULL;
}

int main()
{
pthread_t threads[20];
int t;
printf("%i bottles of beer on the wall\n%i bottles of beer\n", beers, beers);
for (t = 0; t < 20; t++)
{
if (pthread_create(&threads[t], NULL, drink_lots, NULL) == -1)
{
error("Can't create thread ");
}
}
void *result;
for (t = 0; t < 20; t++)
{
if (pthread_join(threads[t], &result) == -1)
{
error("Can't join thread");
}
}
printf("There are now %i bottles of beer on the wall\n", beers);
return 0;
}

// gcc thread.c -lpthread -o thread

Copilot Time

pthread_t 是什么

pthread_t 是 POSIX 线程库中定义的一个数据类型,用于表示线程的 ID。在创建新线程时,pthread_create 函数会返回一个 pthread_t 类型的值,这个值可以用于后续的线程操作,例如等待线程结束(pthread_join)或者取消线程(pthread_cancel)。

为什么要创建线程

创建线程有多种原因:

并发执行:线程可以使多个任务并发执行,这对于提高程序的响应性和性能非常有帮助。例如,一个服务器程序可能需要同时处理多个客户端请求,每个请求可以在一个单独的线程中处理。

简化程序设计:在某些情况下,使用多线程可以简化程序设计。例如,一个图形用户界面程序可能有一个线程用于响应用户输入,另一个线程用于执行后台任务。

利用多核处理器:现代计算机通常有多个处理器核心。通过创建多个线程,程序可以在多个核心上并行执行,从而充分利用硬件资源。

非阻塞操作:在某些情况下,你可能不希望某个耗时的操作阻塞你的主线程(例如网络请求或大数据计算),这时你可以将这个操作放在一个新的线程中执行,这样主线程就可以继续执行其他任务。

请注意,虽然线程有很多优点,但是也需要注意线程安全和同步问题,以避免数据竞争和死锁等问题。

常用方法

POSIX 线程(pthread)库中,常用的线程相关的函数有:

  • pthread_create:创建一个新的线程。这个函数需要一个 `pthread_t`` 变量来保存新线程的 ID,一个函数指针指向线程函数,以及一个指针传递给线程函数。
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
  • pthread_join:等待一个线程结束。这个函数需要一个 pthread_t 变量来指定要等待的线程,以及一个指针来保存线程的返回值。
void *result;
pthread_join(thread, &result);
  • pthread_mutex_lock:加锁。这个函数需要一个 pthread_mutex_t 变量来指定要加锁的互斥量。
pthread_mutex_lock(&mutex);
  • pthread_mutex_unlock:解锁。这个函数需要一个 pthread_mutex_t 变量来指定要解锁的互斥量。
pthread_mutex_unlock(&mutex);
  • pthread_mutex_init:初始化互斥量。这个函数需要一个 pthread_mutex_t 变量来指定要初始化的互斥量,以及一个 pthread_mutexattr_t 变量来指定互斥量的属性。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • pthread_mutex_destroy:销毁互斥量。这个函数需要一个 pthread_mutex_t 变量来指定要销毁的互斥量。
pthread_mutex_destroy(&mutex);
  • pthread_cancel:取消一个线程。这个函数需要一个 pthread_t 变量来指定要取消的线程。
pthread_cancel(thread);
  • pthread_setcancelstate:设置线程的取消状态。这个函数需要一个 int 类型的变量来指定线程的取消状态,以及一个 int 类型的指针来保存线程的旧的取消状态。
int old_state;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
  • pthread_setcanceltype:设置线程的取消类型。这个函数需要一个 int 类型的变量来指定线程的取消类型,以及一个 int 类型的指针来保存线程的旧的取消类型。
int old_type;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
  • pthread_testcancel:测试线程是否被取消。这个函数没有参数,也没有返回值。
pthread_testcancel();

什么是线程安全

线程安全是指多线程访问同一代码时,不会出现不确定的结果。例如,如果多个线程同时执行 printf 函数,那么输出的结果可能是混乱的,因为 printf 函数内部使用了一个缓冲区,多个线程同时访问这个缓冲区可能会导致输出的结果混乱。这种情况下,我们可以使用互斥量来保证线程安全,即同一时间只有一个线程可以访问 printf 函数。