Linux线程

线程与进程的区别和联系

目的

为了提高计算机的吞吐量。

区别

  1. 内存空间

    • 进程:独享内存空间。
    • 线程:共享内存空间。
  2. 切换开销

    • 进程之间切换:需要时间开销(上下文切换),包括建立进程资源表项、开辟内存空间、打开文件描述符等。
    • 线程之间切换:不需要时间开销。
  3. 执行效率与安全性

    • 线程执行效率比进程要高。
    • 但进程安全性比线程要高,因为一个线程异常退出,会导致整个进程退出,而进程在安全模式下异常退出,其他进程不受影响。
  4. 单位

    • 进程:是资源管理的最小单位。
    • 线程:是执行流的最小单位。

关系

  • 一段程序,至少包括一个进程,而一个进程至少包括一个线程。
  • 识别进程的标识是进程号:getpid()
  • 识别线程的标识是线程号:pthread_self() => unsigned int
  • 是进程包括线程,而不是线程包括进程。

线程属性

  • 动态性
  • 并发性
  • 异步性
  • 共享性

注意

  • 线程属于第三方库,则在编译的时候需要指定库名:
    • -lpthread:线程库
    • -lcrypt:MD5密码库
    • -lm:数学库

创建线程

在C语言中,创建线程可以使用pthread_create函数,其原型如下:

int pthread_create(pthread_t *restrict thread,
                   const pthread_attr_t *restrict attr,
                   void *(*start_routine)(void*),
                   void *restrict arg);


void* thread_function(void* arg) {
    // 打印消息
    printf("Hello from the new thread!\n");

    // 线程可以在这里执行其他任务

    // 线程函数返回NULL
    return NULL;
}

int main() {
    pthread_t thread_id;  // 用于存储线程ID
    int result;           // 用于存储pthread_create的返回值

    // 创建线程
    result = pthread_create(&thread_id, NULL, thread_function, NULL);

    // 检查线程是否创建成功
    if (result != 0) {
        perror("Thread creation failed");
        return 1;
    }

    // 等待线程结束
    pthread_join(thread_id, NULL);

    printf("Thread finished executing\n");

    return 0;
}
  1. pthread_t *restrict thread:
    • 类型:pthread_t *
    • 作用:用于保存创建线程的线程号。
    • 注意:线程号不由用户指定,而是由系统分配。
  2. *const pthread_attr_t restrict attr
    • 类型:const pthread_attr_t *
    • 作用:表示创建线程的属性,如线程栈的大小、调度策略、优先级等。
    • 常用值:NULL,表示使用默认属性。
  3. void *(start_routine)(void)
    • 类型:void *(*)(void*)
    • 作用:表示创建线程成功后所执行的函数。
    • 常用值:非NULL,指向线程函数的指针。
  4. *void restrict arg
    • 类型:void *
    • 作用:这个参数是传递给start_routine函数的参数。由于void*类型的通用性,可以通过它传递任何类型的值,但通常需要将其强制转换为正确的类型。
    • 常用值:NULL,表示不传递参数。

返回值

  • 成功:返回0
  • 失败:返回非零值

线程退出与线程等待

线程退出

线程可以通过pthread_exit函数来退出。函数原型如下:

void pthread_exit(void *value_ptr);

void* thread_function(void* arg) {
    // 执行一些任务
    printf("Thread is running...\n");

    // ... 在这里执行更多任务 ...

    // 使用pthread_exit退出线程,并返回一个值
    pthread_exit((void*)42);  // 返回值42作为示例
}

int main()
{
    ...
       
    // 等待线程结束并获取返回值
    result = pthread_join(thread_id, &thread_result);
        
    ...
}
  • 参数
    • value_ptr:类型为void*的指针,用于线程退出时返回一个值。如果不需要返回值,可以传递NULL
  • 注意
    • 不可以返回自定义非静态局部变量的地址,因为这些变量在函数返回后就不存在了,可能会导致未定义行为。

线程等待

pthread_join函数用于等待一个线程结束。函数原型如下:

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


int main()
{
    ...
       
    // 等待线程结束并获取返回值
    result = pthread_join(thread_id, &thread_result);
        
    ...
}
  • 参数

    • thread:类型为pthread_t,表示要等待的线程的线程号。
    • value_ptr:类型为void**的指针,用于保存线程退出的返回值。如果不需要获取返回值,可以传递NULL
  • 返回值

    • 成功:返回0
    • 失败:返回非零值
  • 注意事项:

    • 如果一个线程已经被其他线程等待过,或者它是分离的(通过pthread_detach设置),尝试再次对它调用pthread_join将导致错误。
    • 如果value_ptr参数是NULL,那么子线程的退出状态将被忽略。
    • 调用pthread_join会阻塞调用线程,直到指定的线程终止。

线程注册清理函数

在多线程编程中,可以在线程退出前注册清理函数,以确保在退出时执行某些清理操作。

注册清理函数

使用pthread_cleanup_push函数来注册一个清理函数。

void pthread_cleanup_push(void (*routine)(void*), void *arg);

// 清理函数原型
void cleanup_routine(void *arg) {
    // 打印传递给清理函数的参数
    printf("%s\n", (char*)arg);
}

void* thread_function(void *arg) {
    // 注册清理函数
    pthread_cleanup_push(cleanup_routine, "Cleanup function called");

    // 执行一些任务
    printf("Thread is working...\n");

    // 模拟线程需要执行清理的情况
    // 在这里可以放置线程的主要工作代码

    // 取消注册的清理函数,并且不执行它(因为第二个参数是0)
    pthread_cleanup_pop(0);

    // 返回一个值,表示线程的执行结果
    return (void*)42;
}
  • 参数:
    • routine:类型为void (*)(void*)的函数指针,表示清理时执行的函数。
    • arg:类型为void*的指针,表示传递给清理函数的参数。

执行清理函数

使用pthread_cleanup_pop函数来执行注册的清理函数。

void pthread_cleanup_pop(int execute);
  • 参数:
    • execute:一个整数,用于控制是否执行清理函数。
      • 0:表示不执行清理函数。
      • 0:表示执行清理函数。

注意事项

  1. 线程执行清理函数与代码放置的位置有关,且必须在注册清理函数之后的代码位置。
  2. 注册函数和执行函数必须一一对应。
  3. 注册函数的顺序和执行函数的顺序是相反的,即最后注册的清理函数会最先执行。

线程互斥锁

互斥锁(Mutex)是用于同步线程的一种机制,它确保同一时间只有一个线程可以访问共享资源。

动态锁

初始化锁

使用pthread_mutex_init函数来初始化一个互斥锁。

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                       const pthread_mutexattr_t *restrict attr);

pthread_mutex_t mutex;

int main() {
    // 初始化互斥锁
    int result = pthread_mutex_init(&mutex, NULL);
    if (result != 0) {
        perror("Mutex initialization failed");
        return 1;
    }

    // 使用互斥锁进行线程同步的代码可以放在这里

    // 销毁互斥锁
    result = pthread_mutex_destroy(&mutex);
    if (result != 0) {
        perror("Mutex destruction failed");
        return 1;
    }

    return 0;
}
  • 参数:
    • mutex:类型为pthread_mutex_t *的指针,指向要初始化的互斥锁变量。
    • attr:类型为pthread_mutexattr_t *的指针,指向互斥锁的属性。如果传递NULL,则表示使用默认属性。
  • 返回值:
    • 成功:返回0
    • 失败:返回非零值

销毁锁

使用pthread_mutex_destroy函数来销毁一个互斥锁。

int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • 参数:
    • mutex:类型为pthread_mutex_t *的指针,指向要销毁的互斥锁变量。
  • 返回值:
    • 成功:返回0
    • 失败:返回非零值

设置加锁和解锁

加锁

使用pthread_mutex_lock函数来对互斥锁加锁。

int pthread_mutex_lock(pthread_mutex_t *mutex);

void* thread_function(void* arg) {
    // 尝试对互斥锁加锁
    int result = pthread_mutex_lock(&mutex);
    if (result != 0) {
        perror("Mutex lock failed");
        return NULL;
    }

    // 互斥锁已加锁,可以安全地访问共享资源

    printf("Thread %ld has the lock\n", (long)arg);

    // 模拟执行一些任务

    // 解锁互斥锁
    result = pthread_mutex_unlock(&mutex);
    if (result != 0) {
        perror("Mutex unlock failed");
        return NULL;
    }

    return NULL;
}
  • 参数:
    • mutex:类型为pthread_mutex_t *的指针,指向要加锁的互斥锁变量。
  • 返回值:
    • 成功:返回0
    • 失败:返回非零值
尝试加锁

使用pthread_mutex_trylock函数来尝试对互斥锁加锁。

int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • 参数:
    • mutex:类型为pthread_mutex_t *的指针,指向要尝试加锁的互斥锁变量。
  • 返回值:
    • 成功:返回0
    • 失败:返回非零值
解锁

使用pthread_mutex_unlock函数来对互斥锁解锁。

int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 参数:
    • mutex:类型为pthread_mutex_t *的指针,指向要解锁的互斥锁变量。
  • 返回值:
    • 成功:返回0
    • 失败:返回非零值

静态锁

可以使用宏PTHREAD_MUTEX_INITIALIZER来静态初始化一个互斥锁。

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值