- 线程实质上是进程内一条独立的执行路径,它可以被处理器独立的调度,同时共享进程里的资源。
- 一个进程至少有一个执行线程,线程是在进程内部运行的,本质上是在进程地址空间内运行的。
- 进程是资源分配的基本单位,线程是调度的基本单位。
- 线程几乎不独立拥有资源
#include
#include
#include
#include
#include
void *thread_function(void *arg);
char message[]="Hello World";
int main(){
int res;
pthread_t a_thread;
void *thread_result;
res=pthread_create(&a_thread,NULL,thread_function,(void*)message);
if(res!=0){
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Writing for thread to finish\n");
res=pthread_join(a_thread,&thread_result);
if(res!=0){
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("THread joined,it returned %s\n",(char*)thread_result);
printf("Message is now %s\n",message);
exit(EXIT_SUCCESS);
}
void * thread_function(void *arg){
printf("thread_function is running.Argument was %s\n",(char*)arg);
sleep(3);
strcpy(message,"Bye!");
//这个是等待线程结束的时候的返回值,也就是thread_result的值
pthread_exit("Thank you for the CPU time");
}
[cch@aubin thread]$ gcc thread1.c -pthread
[cch@aubin thread]$ ./a.out
Writing for thread to finish
thread_function is running.Argument was Hello World
THread joined,it returned Thank you for the CPU time
Message is now Bye!
[cch@aubin thread]$
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
第一个参数是线程的类型,第二个参数是线程的属性,第三个是函数指针(线程的入口函数),第四个是给函数指针的参数。该函数的成功会返回一个0,错误会返回一个错误码。
int pthread_join(pthread_t thread, void **retval);
以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。参数 :thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。返回值 : 0代表成功。 失败,返回的则是错误号。
void pthread_exit(void *value_ptr);
线程内部调用该函数用于退出当前线程,参数value_ptr是用于存储线程的退出值传递出去,也可以设为NULL,调用接下来的pthread_join回收线程就可以传出到参数void **value_ptr中。
进程和线程的区别
- 进程是资源调度的基本单位,每一个进程独立的拥有资源。而同一个进程中的线程会共享进程中的资源,包括全局的变量。
pthread_join与pthread_detach的区别
- pthread_create创建线程有两种状态,joinable和unjoinable,也就是可结和与分离的区别,默认是joinable状态。
- 一个可结合的线程能够被其他线程收回其资源和杀死,在被其他线程回收之前,他的存储器资源是不可释放的,所以以默认的属性创建线程时,创建的线程是可结合的,我们需要对线程退出时用pthread_join对线程资源进行回收,只有当pthread_join函数返回的时候,创建的线程才算终止,才会释放自己占用的系统资源。
- 一个不可结合的线程,线程结束后是会自动释放占用的资源。
#include
#include
#include
void* thread_function(void* arg);
int main(){
int res;
pthread_t a_thread;
void *thread_result;
res=pthread_create(&a_thread,NULL,thread_function,NULL);
if(res!=0){
printf("Thread creation failed\n");
exit(EXIT_FAILURE);
}
//res=pthread_detach(a_thread);
res=pthread_join(a_thread,&thread_result);
if(res!=0){
printf("Thread detach failed\n");
exit(EXIT_FAILURE);
}
printf("main finish\n");
while(1){
printf("main running...\n");
sleep(1);
}
exit(EXIT_SUCCESS);
}
void* thread_function(void* arg){
while(1){
printf("thread running...\n");
sleep(1);
}
exit(EXIT_SUCCESS);
}
# pthread_detach运行结果
[cch@aubin thread]$ gcc thread2.c -pthread
[cch@aubin thread]$ ./a.out
main finish
main running...
thread running...
thread running...
main running...
thread running...
main running...
main running...
thread running...
thread running...
main running...
thread running...
main running...
thread running...
main running...
main running...
thread running...
main running...
thread running...
thread running...
main running...
^C
# pthread_join运行结果
[cch@aubin thread]$ gcc thread2.c -pthread
[cch@aubin thread]$ ./a.out
thread running...
thread running...
thread running...
thread running...
thread running...
thread running...
thread running...
thread running...
thread running...
thread running...
^C
[cch@aubin thread]$
进程的资源对进程中的线程是共享的,当线程对进程中的资源进行修改时,如果不对线程的执行的顺序加以控制,会导致数据的混乱。
#include
#include
#include
#include
#include
void *thread_function(void *arg);
int sign=1;//信号量
char message[]="Hello World";
int main(){
int res;
pthread_t a_thread;
void *thread_result;
int print_count1=0;//父线程私有
res=pthread_create(&a_thread,NULL,thread_function,(void*)message);
if(res!=0){
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
while(print_count1++<20){
if(sign==1){
printf("1 ");
fflush(stdout);//强制性输出
sign=2;
}else sleep(1);
}
//printf("Writing for thread to finish\n");
res=pthread_join(a_thread,&thread_result);
if(res!=0){
perror("Thread join failed");
exit(EXIT_FAILURE);
}
//printf("THread joined,it returned %s\n",(char*)thread_result);
//printf("Message is now %s\n",message);
exit(EXIT_SUCCESS);
}
void * thread_function(void *arg){
int print_count2=0;//子线程私有
while(print_count2++<20){
if(sign==2){
fflush(stdout);//强制性输出
printf("2 ");
sign=1;
}else sleep(1);
}
sleep(3);
}
[cch@aubin thread]$ gcc thread1.c -pthread
[cch@aubin thread]$ ./a.out
1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 [cch@aubin thread]$
现象:在执行过程中,上述数据是一次性打印的,不是一个个蹦出来的,这是因为linux是有缓冲的,频繁的输入输出会耗费资源,降低性能,所以linux会等待一批数据,然后一次性输出一批数据。可以使用
fflush(stdout);//强制性输出
#include
#include
#include
#include
#include
#include
void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main(){
int res;
pthread_t a_thread;
void *thread_result;
res=sem_init(&bin_sem,0,0);//信号量初始化为0
if(res!=0){
printf("Semaphore initialization failed");
exit(EXIT_FAILURE);
}
res=pthread_create(&a_thread,NULL,thread_function,NULL);
if(res!=0){
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish\n");
while(strncmp("end",work_area,3)!=0){
fgets(work_area,WORK_SIZE,stdin);
sem_post(&bin_sem);
}
printf("\nWaiting for thread to finish\n");
res=pthread_join(a_thread,&thread_result);
if(res!=0){
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread join\n");
sem_destroy(&bin_sem);//清除信号资源
exit(EXIT_SUCCESS);
}
void * thread_function(void *arg){
sem_wait(&bin_sem);
while(strncmp("end",work_area,3)!=0){
printf("You input %d characters\n",strlen(work_area)-1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}
Input some text. Enter 'end' to finish
shhhhiiihwoojaend
You input 17 characters
ajjq
You input 4 characters
end
Waiting for thread to finish
Thread join
[cch@aubin thread]$
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem :指向信号量对象;pshared : 指明信号量的类型。不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享。value : 指定信号量值的大小。sem_init() 成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。int sem_post(sem_t *sem);
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;sem_post() 成功时返回 0;错误时,信号量的值没有更改,-1 被返回,并设置 errno 来指明错误。也即是PV操作中的V操作。int sem_wait(sem_t * sem);
sem_wait是一个函数,也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。也就是相当于PV操作中的P操作。函数成功返回0,错误的话信号量的值不改动,返回-1.errno设定来标识错误.#include
///< 创建互斥对象,用指定的初始化属性初始化互斥对象
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
///< 加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
///< 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
///< 加锁,但是如果对象已经上锁则返回EBUSY错误代码而不阻塞
int pthread_mmutex_trylock(pthread_mutex_t *mutex);
///< 析构并释放mutex相关资源
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥锁的使用
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL); ///< 初始化互斥锁
pthread_mutex_lock(&mutex); ///< 加锁
///< 操作公共资源
pthread_mutex_unlock(&mutex); ///< 解锁
pthread_mutex_destroy(&mutex); ///< 销毁互斥锁
#include
#include
#include
#include
#define QUEUE_SIZE 10 //定义缓冲区的大小
int buffer[QUEUE_SIZE];//缓冲区数组
int in=0,out=0;
sem_t full,empty;
pthread_mutex_t lock;//互斥锁
int produce=0;
//生产者线程函数
void *producer(void* arg){
while(1){
produce++;//生产新的产品
sem_wait(&empty);//V
pthread_mutex_lock(&lock);//加锁
buffer[in]=produce;//写入缓冲区
in=(in+1)%QUEUE_SIZE;//更新输入队列指针
printf("Produced %d\n",produce);
pthread_mutex_unlock(&lock);
sem_post(&full);
sleep(2);//等待一段时间,生产下一个数据
}
}
void* consumer(void*arg){
while(1){
sem_wait(&full);
pthread_mutex_lock(&lock);
int consume=buffer[out];//从缓冲区中取数据
out=(out+1)%QUEUE_SIZE;
printf("Comsumed %d\n",consume);
pthread_mutex_unlock(&lock);
sem_post(&empty);
sleep(3);
}
}
int main(){
int res;
sem_init(&empty,0,QUEUE_SIZE);//初始化信号量
sem_init(&full,0,0);
pthread_mutex_init(&lock,NULL);//初始化互斥锁
pthread_t producer_thread,consumer_thread;
//创建
printf("starting......\n");
res=pthread_create(&producer_thread,NULL,&producer,NULL);
if(res!=0){
perror("Produce thread creation failed");
exit(EXIT_FAILURE);
}
res=pthread_create(&consumer_thread,NULL,&consumer,NULL);
if(res!=0){
perror("Consumer thread creation failed");
exit(EXIT_FAILURE);
}
//阻塞
res=pthread_join(producer_thread,NULL);
if(res!=0){
perror("Produce thread join failed");
exit(EXIT_FAILURE);
}
res=pthread_join(consumer_thread,NULL);
if(res!=0){
perror("Consumer thread join failed");
exit(EXIT_FAILURE);
}
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&lock);
exit(EXIT_SUCCESS);
}
[cch@aubin thread]$ ./a.out
starting......
Produced 1
Comsumed 1
Produced 2
Comsumed 2
Produced 3
Comsumed 3
Produced 4
Produced 5
Comsumed 4
Produced 6
Comsumed 5
Produced 7
Produced 8
Comsumed 6
Produced 9
Comsumed 7
Produced 10
Produced 11
Comsumed 8
Produced 12
^C
阅读以下代码,确定x的值,并在此基础上,说明进程和线程之间的区别
在以上代码中,x的值最后为-1。x的值在主线程中初始化为0,然后在主线程中设为1,在创建的线程fun中设置为-1。
线程和进程的区别在于:线程是调度的基本单位,进程是资源分配的基本单位;进程是程序的执行实例,是独立拥有存储空间的运行单位,每一个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据;线程是进程的一条的一条独立路径,线程几乎不独立拥有资源,线程共享进程的资源,包括文件描述符等,线程共享相同的地址空间,因此以上的x是会被主线程和其他线程共享。
如何进行线程的同步
①可以使用信号量控制线程的执行顺序,比如sem_init初始化一个信号量;sem_post相当于原子操作的V操作;sem_wait相当于原子操作的P操作;
#include
#include
#include
#include
#include
#include
void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main(){
int res;
pthread_t a_thread;
void *thread_result;
res=sem_init(&bin_sem,0,0);//信号量初始化为0
if(res!=0){
printf("Semaphore initialization failed");
exit(EXIT_FAILURE);
}
res=pthread_create(&a_thread,NULL,thread_function,NULL);
if(res!=0){
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish\n");
while(strncmp("end",work_area,3)!=0){
fgets(work_area,WORK_SIZE,stdin);
sem_post(&bin_sem);
}
printf("\nWaiting for thread to finish\n");
res=pthread_join(a_thread,&thread_result);
if(res!=0){
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread join\n");
sem_destroy(&bin_sem);//清除信号资源
exit(EXIT_SUCCESS);
}
void * thread_function(void *arg){
sem_wait(&bin_sem);
while(strncmp("end",work_area,3)!=0){
printf("You input %d characters\n",strlen(work_area)-1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}
②也可以使用互斥锁进行同步:pthread_mutex_init用来初始化一个互斥锁,pthread_mutex_lock、pthread_mutex_unlock进行加锁和解锁操作,pthread_mutex_destroy用来释放锁操作。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL); ///< 初始化互斥锁
pthread_mutex_lock(&mutex); ///< 加锁
///< 操作公共资源
pthread_mutex_unlock(&mutex); ///< 解锁
pthread_mutex_destroy(&mutex); ///< 销毁互斥锁
请设计使用进程和线程实现相同目标的程序,请说明你对进程/线程应用范围的看法
使用进程(在C中使用fork):
#include
#include
#include
int main() {
int x = 0;
if (fork() == 0) {
// 子进程
x++;
printf("子进程:x = %d\n", x);
} else {
// 父进程
wait(NULL);
printf("父进程:x = %d\n", x);
}
return 0;
}
[cch@aubin os]$ gcc demo.c
[cch@aubin os]$ ./a.out
子进程:x = 1
父进程:x = 0
[cch@aubin os]$
使用线程(在C中使用pthread):
#include
#include
int x = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *increment(void *arg) {
pthread_mutex_lock(&mutex);
x++;
printf("线程:x = %d\n", x);
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, increment, NULL);
pthread_create(&thread2, NULL, increment, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("主线程:x = %d\n", x);
return 0;
}
[cch@aubin os]$ ./a.out
线程:x = 1
线程:x = 2
主线程:x = 2
[cch@aubin os]$
进程的应用范围:
并行计算: 进程适用于需要充分利用多核处理器进行并行计算的场景。每个进程有独立的内存空间,可以并行执行,提高计算效率。
稳定性: 进程之间的独立性可以提高程序的稳定性。一个进程的崩溃不会影响其他进程的执行。
资源隔离: 进程之间有独立的内存空间,可以更好地实现资源隔离,确保一个进程的错误不会波及到其他进程。
线程的应用范围:
轻量级任务: 线程适用于需要执行相对轻量级任务的场景,例如I/O密集型任务。线程的创建和切换开销较小。
共享内存: 线程之间共享同一进程的内存空间,适合在同一应用程序内共享数据。
响应性: 线程可用于实现更快的响应时间,例如在GUI应用程序中响应用户输入。