Linux网络编程笔记9 | 多线程

本文详细介绍了Linux下的多线程编程,包括线程的概念、特点、POSIX线程标准、线程的创建、汇合、分离、线程ID、线程终止以及线程间的同步与通信机制如互斥锁、信号量和条件变量。同时讨论了线程冲突、死锁问题以及线程调度的基本原理,为理解多线程编程提供了深入的见解。
摘要由CSDN通过智能技术生成

十四、多线程

1.什么是线程?

源代码 -编译和链接-> 程序 -加载到内存中-> 进程
                    |                    |
                   文件                 内存
                                       /   \
                                     代码 <- 执行
                                     数据 <- 处理
                                      |      | <- CPU
                                     静态   动态
                                      |       |
                                     资源    线程

线程就是进程的执行过程,即进程内部的控制序列,或者说是进程中的一个任务。
一个进程可以同时拥有多个线程,即同时被系统调度的多个执行路径,但至少要有一个主线程——main函数及被其调用的其它函数。

一个进程的所有线程都共享进程的代码区、数据区、BSS区、堆区、环境变量和命令行参数区、文件描述符表、信号处理函数、当前工作目录、用户和组的各种ID等。但是栈区不是共享的,一个进程的每个线程都拥有自己独立的栈区。

线程调度:
1)系统内核中专门负责线程调度的处理单元被称为调度器;
2)调度器将所有处于就绪状态(没有阻塞在任何系统调用上)的线程排成一个队列,即所谓就绪队列;
3)调度器从 就绪队列中获取队首线程,为其分配一个时间片,并令处理器执行该线程,过了一段时间:
A.该线程的时间片耗尽,调度器立即终止该线程,并将其排到就绪队列的尾端,接着从队首获取下一个线程;
B.该线程的时间片未耗尽,但需阻塞于某系统调用,比如等待I/O或者睡眠。调度器会中止该线程,并将其从就绪队列中移至等待队列,直到其等待的条件满足后,再被移回就绪队列;
4)在低优先级线程执行期间,有高优先级线程就绪,后者会抢占前者的时间片;
5)若就绪队列为空,则系统内核进入空闲状态,直至其非空;
6)像Linux这样的多任务分时系统,基本的调度单位是线程;
7)为线程分配的时间片不宜过长,因为时间片太长会导致没有获得处理机的线程等候时间过久,降低系统运行的并行性,用户会感觉到明显的响应延迟;时间片也不宜过短,因为过短的时间片会增加在线程之间切换上下文的频率,降低系统的运行性能。

2.线程的基本特点

1)线程是进程中的独立实体,可以拥有自己的资源,可以被独立标识——线程ID,同时也被作为基本调用单元,参数时间片的分配。
2)线程有不同的状态,如创建、运行、终止、暂停、恢复、取消等。
3)线程可以使用的大部分资源还是隶属于进程的,因此线程作为进程的一部分不能脱离进程独立存在。
4)一个进程可以同时执行多个线程,这些线程可以执行相同的代码,完成相同的任务,也可以执行不同的代码,完成不同的任务。
5)创建一个线程所花费的开销远小于创建进程的开销。线程也称为轻量级进程。因此在解决诸如并发问题等问题时,优先考虑多线程,其次才是多进程。
6)多线程的问题在于因为太多的资源被共享,极易导致冲突,为了解决冲突可能需要增加额外的开销,因此多进程仍然有它的优势。
进程,内存壁垒,通信。
线程,内存共享,同步。

3.POSIX线程

#include <pthread.h>
-lpthread -> libpthread.so

4.创建线程

线程过程函数:在一个线程中被内核调用的函数,对该函数的调用过程就是线程的执行过程,从该函数中返回就意味着线程的结束。因此,main函数其实就是一个进程的主线程的线程过程函数。所有

自创建的线程都必须有一个对应线程过程函数。
void* 线程过程函数(void* 线程参数指针) {
线程的执行过程
}
int pthread_create(pthread_t* tid,
const pthread_attr_t* attr,
void* (start_routine)(void),
void* arg);
成功返回0,失败返回错误码。
tid - 输出线程标识(TID)。
attr - 线程属性,NULL表示缺省属性。
start_routine - 线程过程函数指针
arg - 线程参数指针
pthread_create
->创建一个新线程
->调用线程过程函数(start_routine)
并传入线程参数指针(arg)
被创建的子线程和创建该子线程的父线程是并行的关系,其调度顺序无法预知,因此当pthread_create函数返回时子线程执行的位置无从确定,其线程过程函数可能尚未被调用,也可能正在执行,甚至可能已经返回。传递给线程的参数对象,一定要在线程过程函数不再使用它的情况下才能被释放。
代码:create.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* thread_proc(void* arg) {
   
    printf("%lu线程:%s\n",
        pthread_self(), (char*)arg);//获取自身的tid
    return NULL;
}
int main(void) {
   
    pthread_t tid;
    int error = pthread_create(&tid, NULL,
        thread_proc, "我是子线程!");
    if (error) {
   
        fprintf(stderr, "pthread_create: %s\n",
            strerror(error));
        return -1;
    }
    printf("%lu线程:我是主线程,"
        "创建%lu线程。\n", pthread_self(), tid);
    sleep(1);//主线程一旦终止,进程就结束。
    return 0;
}

主线程和通过pthread_create函数创建的多个子线程,在时间上“同时”运行,如果不附加任何同步条件,则它们每一个执行步骤的先后顺序完全无法预知,这就叫做自由并发。
代码:concur.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* thread_proc(void* arg) {
   
    for (;;) {
   
        printf("%d", (int)arg);
        usleep((rand() % 100) * 1000);
    }
    return NULL;
}
int main(void) {
   
    srand(time(NULL));
    setbuf(stdout, NULL);
    pthread_t tid;
    for (int i = 0; i < 5; ++i)
        pthread_create(&tid, NULL, thread_proc,
            (void*)(i+1));
    getchar();
    return 0;
}

为了让线程过程函数的实现更加灵活,可以通过线程参数传递特定的信息,帮助线程过程函数执行不同的任务。
代码:arg.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <pthread.h>
#define PI 3.14159
void* thread_area(void* arg) {
   
    double r = *(double*)arg;
    *(double*)arg = PI * r * r;
    return NULL;
}
struct Pyth {
   
    double a, b, c;
};
void* thread_pyth(void* arg) {
   
    struct Pyth* pyth = (struct Pyth*)arg;
    pyth->c = sqrt((pyth->a *
        pyth->a + pyth->b * pyth->b));
    return NULL;
}
void* thread_aver(void* arg) {
   
    double* d = (double*)arg;
    d[2] = (d[0] + d[1]) / 2;
    return NULL;
}
void* thread_show(void* arg) {
   
    printf("%d\n", *(int*)arg);
    return NULL;
}
int main(void) {
   
    pthread_t tid;
    double r = 10;
    pthread_create(&tid, NULL, thread_area, &r);
    usleep(100000);
    printf("%g\n", r);
    
    struct Pyth pyth = {
   3, 4};
    pthread_create(&tid, NULL, thread_pyth,
        &pyth);
    usleep(100000);
    printf("%g\n", pyth.c);
    
    double d[3] = {
   123, 456};
    pthread_create(&tid, NULL, thread_aver
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值