一、进程(Process)
-
概念
进程是计算机中程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。它可以理解为一个正在执行的程序实例,拥有自己独立的地址空间、代码段、数据段、堆栈等资源。例如,当你打开一个文本编辑器,操作系统就会为这个文本编辑器创建一个进程,这个进程有它自己的内存空间来存储正在编辑的文本内容、程序的代码以及各种运行时的数据。 -
进程的特点
- 独立性:不同进程之间的地址空间相互独立,一个进程不能直接访问另一个进程的内存数据,这保障了进程之间不会相互干扰。
- 资源拥有性:进程拥有系统分配的各种资源,如 CPU 时间片、内存、文件句柄等。
- 并发性:多个进程可以在宏观上同时运行,操作系统通过调度算法在多个进程之间切换 CPU 资源,使得每个进程都能得到执行机会。
二、线程(Thread)
-
概念
线程是进程中的一个执行单元,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的地址空间和资源,如代码段、数据段和打开的文件等。线程可以看作是在进程内部并发执行的路径,它们协同工作来完成进程的任务。例如,在一个网络浏览器进程中,可能有一个线程负责接收用户输入(如输入网址),一个线程负责从服务器下载网页数据,还有一个线程负责渲染页面,这些线程共同完成浏览器的功能。 -
线程的特点
- 轻量级:线程比进程更轻量级,创建和销毁线程的开销比进程小得多。因为线程共享进程的大部分资源,不需要像创建进程那样重新分配大量的系统资源。
- 共享资源:线程之间可以方便地共享进程内的数据,但这也需要注意对共享资源的同步访问问题,否则可能会导致数据不一致。
- 并发性:和进程类似,多个线程在一个进程内可以并发执行,提高了程序的执行效率。
三、创建线程(C 语言使用 POSIX 线程库 pthread)
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
#include <unistd.h>
#include <stdio.h>
#include<pthread.h>
#include<string.h>
//定义线程函数:线程函数是线程执行的代码逻辑,它的返回值类型是 void *,参数类型是 void *。例如:
void *rout(void* args)
{int i;for(; ;) {printf("I am thread-%d",i);sleep(1);// 获取当前线程的线程IDpthread_t t = pthread_self();}
}
int main() {pthread_t tid;int ret;ret=pthread_create(&tid,NULL,&rout,NULL);if(ret<0){}int i;for(; ;){printf("main thread\n");sleep(1);}return 0;}
四、多线程的其他操作
1.线程等待(pthread_join)
使用 pthread_join
函数可以让一个线程等待另一个线程结束。其函数原型为:int pthread_join(pthread_t thread, void **retval);
其中,thread
是要等待的线程标识符,retval
是用于接收被等待线程的返回值(如果线程函数有返回值的话)的指针。例如:
#include <stdio.h>
#include <pthread.h>void *thread_function(void *arg) {for (int i = 0; i < 10; i++) {printf("This is a thread: %d\n", i);}return NULL;
}int main() {pthread_t thread_id;pthread_create(&thread_id, NULL, thread_function, NULL);void *thread_result;pthread_join(thread_id, &thread_result);for (int i = 0; i < 10; i++) {printf("This is the main thread: %d\n", i);}return 0;
}
在这个例子中,主线程会等待新创建的线程执行完毕后再继续执行自己的循环
2.线程终止(pthread_exit)
线程可以通过调用 pthread_exit
函数来终止自己。其函数原型为:void pthread_exit(void *retval);
例如:
#include <stdio.h>
#include <pthread.h>void *thread_function(void *arg) {for (int i = 0; i < 5; i++) {printf("This is a thread: %d\n", i);}pthread_exit(NULL);
}int main() {pthread_t thread_id;pthread_create(&thread_id, NULL, thread_function, NULL);void *thread_result;pthread_join(thread_id, &thread_result);printf("Thread has terminated.\n");return 0;
}
在这个线程函数中,线程执行到 pthread_exit
时就会终止,主线程会通过 pthread_join
检测到线程结束。
3.线程同步(互斥锁 pthread_mutex)
同步问题是保证数据安全的情况下,让我们的线程访问资源具有一定的顺序性。
初始化互斥锁:使用 pthread_mutex_init
函数初始化互斥锁,例如:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
(这种静态初始化方式适用于简单情况)或者
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
加锁和解锁:在访问共享资源之前使用 pthread_mutex_lock
函数加锁,访问完成后使用 pthread_mutex_unlock
函数解锁。例如:
售票存在问题:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;
void *route(void *arg)
{char *id = (char*)arg;while ( 1 ) {if ( ticket > 0 ) {usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;} else {break;}}
}
int main( void )
{pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, "thread 1");pthread_create(&t2, NULL, route, "thread 2");pthread_create(&t3, NULL, route, "thread 3");pthread_create(&t4, NULL, route, "thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);
}
解决办法:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int ticket=100;
pthread_mutex_t mutex;
void *route(void*arg)
{char *id =(char*)arg;while(1){pthread_mutex_lock(&mutex);if(ticket>0){usleep(1000);printf("%s sells ticket:%d\n",id,ticket);ticket--;pthread_mutex_unlock(&mutex);}else{exit(1);pthread_mutex_unlock(&mutex);}}
}
int main()
{pthread_t t1,t2,t3,t4;pthread_mutex_init(&mutex,NULL);pthread_create(&t1,NULL,route,"thread 1");pthread_create(&t2,NULL,route,"thread 2");pthread_create(&t3,NULL,route,"thread 3");pthread_create(&t4,NULL,route,"thread 4");pthread_join(t1,NULL);pthread_join(t2,NULL);pthread_join(t3,NULL);pthread_join(t4,NULL);pthread_mutex_destroy(&mutex);
}
4.线程间的条件变量
条件变量用于线程间的同步,通常与互斥锁一起使用。一个线程可以等待某个条件变量满足,而另一个线程可以在条件满足时唤醒等待该条件变量的线程。
初始化条件变量:使用 pthread_cond_init
函数初始化条件变量,例如:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
(静态初始化)或者
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
等待条件变量:使用 pthread_cond_wait
函数等待条件变量,它会自动释放互斥锁并阻塞线程,直到条件变量被唤醒。例如
pthread_mutex_t mutex;
pthread_cond_t cond;
int data_ready = 0;void *thread_function(void *arg) {pthread_mutex_lock(&mutex);while (!data_ready) {pthread_cond_wait(&cond, &mutex);}// 这里是条件满足后执行的代码pthread_mutex_unlock(&mutex);return NULL;
}int main() {// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_t thread;pthread_create(&thread, NULL, thread_function, NULL);// 模拟数据准备好的情况pthread_mutex_lock(&mutex);data_ready = 1;pthread_cond_signal(&cond); // 唤醒等待条件变量的线程pthread_mutex_unlock(&mutex);pthread_join(thread, NULL);return 0;
}
在这个例子中,一个线程等待 data_ready
条件满足,主线程在准备好数据后通过 pthread_cond_signal
唤醒等待的线程。