LinuxC线程编程


在这里插入图片描述

一、线程创建

#include <pthread.h>

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

Compile and link with -pthread.

pthread_create()函数用于创建一个线程:

  • thread:指向线程标识符的指针。
  • attr:用来设置线程的属性。
  • start_routine:一个函数指针,代表线程运行函数的起始地址。
  • arg:线程运行函数的参数。

若线程创建成功,则返回0;若线程创建失败,则返回出错编号。
使用示例

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *thread_fun(void *arg) {
    
    
    printf("My pid is %d, my tid is %u.\n", getpid(), *((unsigned int *)arg));
}

int main() {
    
    
    pthread_t tid;

    printf("My pid is %d, my tid is %u.\n", getpid(), (unsigned int)pthread_self());

    if (pthread_create(&tid, NULL, thread_fun, (void *)(&tid)) != 0) {
    
    
        perror("Create error");
        exit(1);
    }

    pause();

    return 0;
}

执行结果

atreus@atreus-virtual-machine:~/code/220317$ make
gcc main.c -o main -pthread
atreus@atreus-virtual-machine:~/code/220317$ ./main
My pid is 54660, my tid is 1044952896.
My pid is 54660, my tid is 1044948736.
^C
atreus@atreus-virtual-machine:~/code/220317$

二、线程退出

#include <pthread.h>

int pthread_cancel(pthread_t thread);
void pthread_exit(void *retval);

Compile and link with -pthread.

pthread_cancel()会发送终止信号给目标线程,如果成功则返回0,否则为非0值。但发送成功并不意味着目标线程一定会终止,目标线程收到终止信号后可以终止,也可以忽略,由其自己决定。线程取消是被动退出。
pthread_exit()函数用于线程的主动退出,可以指定返回值,其他线程可以通过pthread_join()函数获取该线程的返回值。当然,对于线程函数使用return返回也可以使线程退出。但不能使用exit退出,会导致进程退出。

三、线程等待&线程状态

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);
int pthread_detach(pthread_t thread);

Compile and link with -pthread.

线程等待的目的就是保证线程的资源能够被回收,线程有两种状态:

  • 可结合态:这种状态下的线程能够被当前进程回收资源或杀死,但线程退出时其资源不会自动释放。
  • 分离态:这种状态下的线程不能被当前进程回收或杀死,但它的存储资源在它终止时可以由系统自动释放。

默认情况下,线程会被创建成可结合态的。可结合线程需要主线程调用pthread_join()函数将其资源回收,或者调用pthread_detach()函数将其变为分离态线程,待线程结束时由系统回收其资源。
使用示例

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *thread1_fun(void *arg) {
    
    
    pthread_detach(pthread_self());//将本线程转换为分离态,由系统自动回收资源
    pthread_exit(NULL);
}

void *thread2_fun(void *arg) {
    
    
    pthread_exit(NULL);
}

int main() {
    
    
    pthread_t tid1, tid2;

    if (pthread_create(&tid1, NULL, thread1_fun, NULL) != 0) {
    
    
        perror("Create error");
        exit(1);
    }

    if (pthread_create(&tid2, NULL, thread2_fun, NULL) != 0) {
    
    
        perror("Create error");
        exit(1);
    }
	/* pthread_detach(tid1); */
    pthread_join(tid2, NULL);//等待子线程的结束,在其结束后回收其资源

    return 0;
}

四、线程同步

匿名信号量

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

Link with -pthread.

sem_init()函数用于初始化一个信号量。sem是指向待操作信号量的指针,pshared如果为0则表示线程间信号量,如果非零则表示定位在共享内存中的进程间信号量,value指定信号量初始化的值。


#include <semaphore.h>

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

Link with -pthread.

sem_wait()函数用于执行P操作,且为阻塞等待。sem_trywait()函数为非阻塞等待,执行失败会直接返回。sem_timedwait()函数也为阻塞等待,但可以通过abs_timeout参数设置等待时间。


#include <semaphore.h>

int sem_post(sem_t *sem);

Link with -pthread.

sem_post()函数用于执行V操作。


#include <semaphore.h>

int sem_destroy(sem_t *sem);

Link with -pthread.

sem_destroy()函数用于销毁一个匿名信号量,在Linux中省略这个函数不会带来异常,但为了安全性和可移植性,还是应该在合适的时机销毁信号量。


通过匿名信号量实现互斥

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

sem_t sem[1];
int sum = 0;

void *thread1_fun(void *arg);

void *thread2_fun(void *arg);

int main() {
    
    
    pthread_t tid1, tid2;

    /* 初始化信号量 */
    sem_init(&sem[0], 0, 1);
    
    /* 创建子线程 */
    if (pthread_create(&tid1, NULL, thread1_fun, NULL) != 0) {
    
    
        perror("pthread_create(&tid1, NULL, thread1_fun, NULL");
        exit(1);
    }
    if (pthread_create(&tid2, NULL, thread2_fun, NULL) != 0) {
    
    
        perror("pthread_create(&tid2, NULL, thread2_fun, NULL");
        exit(1);
    }

    /* 等待子线程退出 */
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    /* 销毁信号量 */
    sem_destroy(&sem[0]);

    return 0;
}

void *thread1_fun(void *arg) {
    
    
    sem_wait(&sem[0]);//P
    printf("[1] sum = %d.\n", sum);
    sleep(1);
    sum++;
    printf("[1] sum = %d.\n", sum);
    sleep(1);
    sem_post(&sem[0]);//V

    pthread_exit(NULL);
}

void *thread2_fun(void *arg) {
    
    
    sem_wait(&sem[0]);//P
    printf("[2] sum = %d.\n", sum);
    sleep(1);
    sum++;
    printf("[2] sum = %d.\n", sum);
    sleep(1);
    sem_post(&sem[0]);//V
    
    pthread_exit(NULL);
}

互斥锁

通过互斥锁实现互斥

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

pthread_mutex_t mutex;
int sum = 0;

void *thread_add_fun(void *arg);

void *thread_show_fun(void *arg);

int main() {
    
    
    pthread_t tid_add, tid_show;

    /* 初始化互斥锁 */
    pthread_mutex_init(&mutex, NULL);

    /* 创建子线程 */
    if (pthread_create(&tid_add, NULL, thread_add_fun, NULL) != 0) {
    
    
        perror("pthread_create(&tid_add, NULL, thread_add_fun, NULL");
        exit(1);
    }
    if (pthread_create(&tid_show, NULL, thread_show_fun, NULL) != 0) {
    
    
        perror("pthread_create(&tid_show, NULL, thread_show_fun, NULL");
        exit(1);
    }

    /* 等待子线程退出 */
    pthread_join(tid_add, NULL);
    pthread_join(tid_show, NULL);

    /* 销毁互斥锁 */
    pthread_mutex_destroy(&mutex);

    return 0;
}

/* 将全局变量增加到3 */
void *thread_add_fun(void *arg) {
    
    
    sleep(1);//将锁让给show线程
    while (sum != 3) {
    
    
        pthread_mutex_lock(&mutex);
        sum = sum + 1;
        printf("[add] sum = %d.\n", sum);
        sleep(1);//给show线程获得锁的机会
        pthread_mutex_unlock(&mutex);
    }
}

/* 当sum增加到3后将sum输出 */
void *thread_show_fun(void *arg) {
    
    
    pthread_mutex_lock(&mutex);
    printf("[show] wait...\n");
    if (sum != 3) pthread_mutex_unlock(&mutex);
    while (1) {
    
    //死循环等待,会一直占用处理机
        pthread_mutex_lock(&mutex);
        if (sum == 3) {
    
    
            /* 满足条件输出后把锁释放 */
            printf("[show] sum = %d.\n", sum);
            pthread_mutex_unlock(&mutex);
            break;
        } else {
    
    
            /* 不满足条件直接释放锁 */
            pthread_mutex_unlock(&mutex);
        }
    }
}

执行结果

atreus@atreus-virtual-machine:~/code/220317$ make
gcc main.c -o main -pthread
atreus@atreus-virtual-machine:~/code/220317$ ./main
[show] wait...
[add] sum = 1.
[add] sum = 2.
[add] sum = 3.
[show] sum = 3.
atreus@atreus-virtual-machine:~/code/220317$

条件变量

通过条件变量和互斥锁实现互斥(改进互斥锁)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int sum = 0;

void *thread_add_fun(void *arg);

void *thread_show_fun(void *arg);

int main() {
    
    
    pthread_t tid_add, tid_show;

    /* 初始化互斥锁和条件变量 */
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    /* 创建子线程 */
    if (pthread_create(&tid_add, NULL, thread_add_fun, NULL) != 0) {
    
    
        perror("pthread_create(&tid_add, NULL, thread_add_fun, NULL");
        exit(1);
    }
    if (pthread_create(&tid_show, NULL, thread_show_fun, NULL) != 0) {
    
    
        perror("pthread_create(&tid_show, NULL, thread_show_fun, NULL");
        exit(1);
    }

    /* 等待子线程退出 */
    pthread_join(tid_add, NULL);
    pthread_join(tid_show, NULL);

    /* 销毁互斥锁和条件变量 */
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

/* 将全局变量增加到3 */
void *thread_add_fun(void *arg) {
    
    
    sleep(1);//将锁让给show线程
    while (sum != 3) {
    
    
        pthread_mutex_lock(&mutex);
        sum = sum + 1;
        printf("[add] sum = %d.\n", sum);
        sleep(1);
        if (sum == 3) pthread_cond_signal(&cond);//满足要求,唤醒show线程
        pthread_mutex_unlock(&mutex);
    }
}

/* 当sum增加到3后将sum输出 */
void *thread_show_fun(void *arg) {
    
    
    pthread_mutex_lock(&mutex);
    printf("[show] wait...\n");
    if (sum != 3) pthread_cond_wait(&cond, &mutex);//不满足要求,挂起自己,等待add线程
    printf("[show] sum = %d.\n", sum);
}

执行结果

atreus@atreus-virtual-machine:~/code/220317$ make
gcc main.c -o main -pthread
atreus@atreus-virtual-machine:~/code/220317$ ./main
[show] wait...
[add] sum = 1.
[add] sum = 2.
[add] sum = 3.
[show] sum = 3.
atreus@atreus-virtual-machine:~/code/220317$

通过执行结果可以看出,show函数在获得锁后发现sum不等于3,于是将锁释放并将自己挂起,add函数获得锁后执行自加,在加到3后唤起show函数完成输出。这样就通过条件变量避免了死循环等待导致的资源占用。

五、进程与线程

区别:

  • 进程资源分配的最小单位,线程任务调度的最小单位。
  • 每个进程拥有独立的地址空间,多个线程共享进程地址空间。
  • 多进程更关注进程间通信多线程更关注资源的保护

如何选择:

  • 需要频繁创建销毁的优先用线程(降低资源消耗)。
  • 高性能交易服务器中间件主张多进程(一个进程的崩溃不会影响其他进程)。
  • 需要进行大量计算时优先使用线程(因为海量计算本身就会耗费大量内存和CPU资源)。
  • 强相关的处理用线程弱相关的处理用进程(视频传输等持久过程属于强相关)。
  • 多机分布进程多核分布用线

猜你喜欢

转载自blog.csdn.net/qq_43686863/article/details/123703103