线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。与多进程相比,多进程具有多进程不具备的一些优点,其最重要的是:对于多线程来说,其能够比多进程更加节省资源。
在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。
在Linux中,通过函数pthread_create()函数实现线程的创建:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
其中:
当线程创建成功时,函数pthread_create()返回0,若返回值不为0则表示创建线程失败。对于线程的属性,则在结构体pthread_attr_t中定义。
线程创建的过程如下所示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
printf("this is a new thread, thread ID is %u\n", newthid);
return NULL;
}
int main(){
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
printf("main thread, ID is %u\n", pthread_self());
for (int i = 0; i < num_thread; i++){
if (pthread_create(&pt[i], NULL, thread, NULL) != 0){
printf("thread create failed!\n");
return 1;
}
}
sleep(2);
free(pt);
return 0;
}
在上述代码中,使用到了pthread_self()函数,该函数的作用是获取本线程的线程ID。在主函数中的sleep()用于将主进程处于等待状态,以让线程执行完成。最终的执行效果如下所示:
那么,如何利用arg向子线程传递参数呢?其具体的实现如下所示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
int num = *(int *)id;
printf("this is a new thread, thread ID is %u,id:%d\n", newthid, num);
return NULL;
}
int main(){
//pthread_t thid;
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
printf("main thread, ID is %u\n", pthread_self());
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!\n");
return 1;
}
}
sleep(2);
free(pt);
free(id);
return 0;
}
其最终的执行效果如下图所示:
如果在主进程提前结束,会出现什么情况呢?如下述的代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
int num = *(int *)id;
printf("this is a new thread, thread ID is %u,id:%d\n", newthid, num);
sleep(2);
printf("thread %u is done!\n", newthid);
return NULL;
}
int main(){
//pthread_t thid;
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
printf("main thread, ID is %u\n", pthread_self());
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!\n");
return 1;
}
}
//sleep(2);
free(pt);
free(id);
return 0;
}
此时,主进程提前结束,进程会将资源回收,此时,线程都将退出执行,运行结果如下所示:
在上述的实现过程中,为了使得主线程能够等待每一个子线程执行完成后再退出,使用了free()函数,在Linux的多线程中,也可以使用pthread_join()函数用于等待其他线程,函数的具体形式为:
int pthread_join(pthread_t thread, void **retval);
函数pthread_join()用来等待一个线程的结束,其调用这将被挂起。
一个线程仅允许一个线程使用pthread_join()等待它的终止。
如需要在主线程中等待每一个子线程的结束,如下述代码所示:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
void* thread(void *id){
pthread_t newthid;
newthid = pthread_self();
int num = *(int *)id;
printf("this is a new thread, thread ID is %u,id:%d\n", newthid, num);
free(3);
printf("thread %u is done\n", newthid);
return NULL;
}
int main(){
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
printf("main thread, ID is %u\n", pthread_self());
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!\n");
return 1;
}
}
for (int i = 0; i < num_thread; i++){
pthread_join(pt[i], NULL);
}
free(pt);
free(id);
return 0;
}
最终的执行效果如下所示:
注:在编译的时候需要链接libpthread.a:
g++ xx.cc -lpthread -o xx
多线程的最大的特点是资源的共享,但是,当多个线程同时去操作(同时去改变)一个临界资源时,会破坏临界资源。如利用多线程同时写一个文件:
#include <stdio.h>
#include <pthread.h>
#include <malloc.h>
const char filename[] = "hello";
void* thread(void *id){
int num = *(int *)id;
// 写文件的操作
FILE *fp = fopen(filename, "a+");
int start = *((int *)id);
int end = start + 1;
setbuf(fp, NULL);// 设置缓冲区的大小
fprintf(stdout, "%d\n", start);
for (int i = (start * 10); i < (end * 10); i ++){
fprintf(fp, "%d\t", i);
}
fprintf(fp, "\n");
fclose(fp);
return NULL;
}
int main(){
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!\n");
return 1;
}
}
for (int i = 0; i < num_thread; i++){
pthread_join(pt[i], NULL);
}
// 释放资源
free(pt);
free(id);
return 0;
}
执行以上的代码,我们会发现,得到的结果是混乱的,出现上述的最主要的原因是,我们在编写多线程代码的过程中,每一个线程都尝试去写同一个文件,这样便出现了上述的问题,这便是共享资源的同步问题,在Linux编程中,线程同步的处理方法包括:信号量,互斥锁和条件变量。
互斥锁是通过锁的机制来实现线程间的同步问题。互斥锁的基本流程为:
其中,在加锁过程中,pthread_mutex_lock()函数和pthread_mutex_trylock()函数的过程略有不同:
同时,解锁的过程中,也需要满足两个条件:
当互斥锁使用完成后,必须进行清除。
有了以上的准备,我们重新实现上述的多线程写操作,其实现代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <malloc.h>
pthread_mutex_t mutex;
const char filename[] = "hello";
void* thread(void *id){
int num = *(int *)id;
// 加锁
if (pthread_mutex_lock(&mutex) != 0){
fprintf(stdout, "lock error!\n");
}
// 写文件的操作
FILE *fp = fopen(filename, "a+");
int start = *((int *)id);
int end = start + 1;
setbuf(fp, NULL);// 设置缓冲区的大小
fprintf(stdout, "%d\n", start);
for (int i = (start * 10); i < (end * 10); i ++){
fprintf(fp, "%d\t", i);
}
fprintf(fp, "\n");
fclose(fp);
// 解锁
pthread_mutex_unlock(&mutex);
return NULL;
}
int main(){
int num_thread = 5;
pthread_t *pt = (pthread_t *)malloc(sizeof(pthread_t) * num_thread);
int * id = (int *)malloc(sizeof(int) * num_thread);
// 初始化互斥锁
if (pthread_mutex_init(&mutex, NULL) != 0){
// 互斥锁初始化失败
free(pt);
free(id);
return 1;
}
for (int i = 0; i < num_thread; i++){
id[i] = i;
if (pthread_create(&pt[i], NULL, thread, &id[i]) != 0){
printf("thread create failed!\n");
return 1;
}
}
for (int i = 0; i < num_thread; i++){
pthread_join(pt[i], NULL);
}
pthread_mutex_destroy(&mutex);
// 释放资源
free(pt);
free(id);
return 0;
}
最终的结果为:
尊敬的博文视点用户您好: 欢迎您访问本站,您在本站点访问过程中遇到任何问题,均可以在本页留言,我们会根据您的意见和建议,对网站进行不断的优化和改进,给您带来更好的访问体验! 同时,您被采纳的意见和建议,管理员也会赠送您相应的积分...
时隔一周,让大家时刻挂念的《Unity3D实战核心技术详解》终于开放预售啦! 这本书不仅满足了很多年轻人的学习欲望,并且与实际开发相结合,能够解决工作中真实遇到的问题。预售期间优惠多多,实在不容错过! Unity 3D实战核心技术详解 ...
如题 ...
读者评论