linux下多线程多进程pthread的使用以及理解

发布于 2018-12-15  428 次阅读


word文档:点击此处进行下载

一些函数的具体用法就不明说了,只做一些核心的解释

下面直接贴代码:

pthread_create():创建一个线程

 

/* thread_create.c */
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

/* 线程函数1 */
void *mythread1(void){
        int i;
        for(i=0;i<5;i++){
                printf("I am the 1st pthread,created by mybeilef321\n");
                sleep(2);
        }
}
/* 线程函数2 */
void *mythread2(void){
        int i;
        for(i=0;i<5;i++){
                printf("I am the 2st pthread,created by mybelief321\n");
                sleep(2);
        }
}
int main(){
        pthread_t id1,id2; /*线程id*/
        int res;
        /*创建一个线程,并使得该线程执行mythread1函数*/
        res=pthread_create(&id1,NULL,(void *)mythread1,NULL);/*返回值: 成功则返回0, 否则返回错误编号.*/
        if(res){
                printf("Create pthread error!\n");
                return 1;
        }
        /*创建一个线程,并使得该线程执行mythread2函数*/
        res=pthread_create(&id2,NULL,(void *)mythread2,NULL);
        if(res){
                printf("Create pthread error!\n");
                return 1;
        }
        /*等待两个线程均退出后,main()函数再退出*/
        pthread_join(id1,NULL);//将线程挂起等待结束
        pthread_join(id2,NULL);
}

这里的 res是创建线程后的返回值: 成功则返回0, 否则返回错误编号.若为0,则执行自己定义的函数

id1,id2是指向新创建线程ID的变量, 作为函数的输出.

使用命令:gcc thread_create.c -o thread_create -lpthread编译,注意不要忘了加 -lpthread,否则会出现如下的错误

/tmp/ccjfZIN3.o: In function `main':
thread_create.c:(.text+0x8b): undefined reference to `pthread_create'
thread_create.c:(.text+0xc0): undefined reference to `pthread_create'
thread_create.c:(.text+0xeb): undefined reference to `pthread_join'
thread_create.c:(.text+0xfc): undefined reference to `pthread_join'
collect2: ld returned 1 exit status

这里使用-lpthread 参数的原因是因为pthread的库不是linux系统的库,所以在进行编译的时候要加上     -lpthread

然后./thread_create会发现:

I am the 2st pthread,created by mybelief321
I am the 1st pthread,created by mybeilef321
I am the 2st pthread,created by mybelief321
I am the 1st pthread,created by mybeilef321
I am the 2st pthread,created by mybelief321
I am the 1st pthread,created by mybeilef321
I am the 2st pthread,created by mybelief321
I am the 1st pthread,created by mybeilef321
I am the 2st pthread,created by mybelief321
I am the 1st pthread,created by mybeilef321

哎!为什么我写的明明是先创建执行第一个线程的函数,却先执行了第二个函数,这是因为线程创建后是同步进行的,但是先执行谁是不确定的。

pthread_exit():退出一个线程

/* thread_exit.c文件 */
#include<stdio.h>
#include<pthread.h>
/*进程函数*/
void *create(void *arg){
        printf("New thread is created...\n");
        pthread_exit((void *)6);/*这里的6也可以设置成其他的数值*/
}
int main()
{
        pthread_t tid;
        int res;
        void *temp;
        res=pthread_create(&tid,NULL,create,NULL);
        printf("I am the main thread!\n");
        if(res){
                printf("thread is not created...\n");
                return -1;
        }
        res=pthread_join(tid,&temp);
        if(res){
                printf("Thread is not exit...\n");
                return -2;
        }
        printf("Thread is exit code %d \n",(int)temp);
        return 0;
}

使用命令:gcc thread_exit.c -o thread_exit -lpthread编译

执行./thread_exit命令(ps:本人比较懒,所以下面就省略编译和执行这一步了)

这里还好理解,一般都可以看得懂,可能在pthread_join这个函数卡住了,下面会讲到,不过这里先简单提一嘴:

pthread_join()用于将当前进程挂起来等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源就被收回。而 *temp则为定义的指针,用来存储被等待线程结束时的返回值(不为NULL时)

运行结果:

I am the main thread!
New thread is created...
Thread is exit code 6 

 

pthread_join():挂起一个线程

噗,本人比较懒嘛。。。所以代码在下载链接里,然后我想说的是,这个函数一定要在 pthread_create()函数后面执行,而且是紧接着的,因为创建完一个线程之后需要将这个线程挂起等待进程的结束。。。。有啥区别可以参考2.0版本和最终版本的代码。。噗。。

互斥锁原理:

好吧,一些原理在文档里面都有,可以自己下载下来去看,毕竟自己看到的才是自己的东西嘛

2.0版本
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define BUF_SIZE 32
#define ASC_SIZE 8
static char buf[BUF_SIZE];

static void delay(){
	int i;
	int t = rand() >> 8;//rand()产生随机数,右移八位,数据变得很大
	for(i=0;i<t;i++)
		i = i;
}

void set_buf(char ch){//对线程进行加锁
	char *p;
	pthread_mutex_lock(&mutex);/*
					pthread_mutex_lock的作用实际就是上锁,
					这个函数和pthread_mutex_unlock配套使用。
					两句函数中间的代码就是被上锁的代码,
					被上锁的代码只能有一个线程使用,别的线程执行到这里会发生阻塞,
					只有unlock之后,别的线程才能使用lock之后进入代码*/
	for(p=buf;p < buf + BUF_SIZE;p++){
		*p = ch;
		putchar(*p);
		//等待putchar执行完
		delay();//等待时间,否则会进行的非常快
	}
	pthread_mutex_unlock(&mutex);//解锁
}
void check(void){/*用于检验加锁后的情况*/
	char ch;
	char *p;
	pthread_mutex_lock(&mutex);//加锁,对共享资源的访问。如果互斥量已经上了锁,调用的线程会阻塞,知道互斥量解锁
	ch = buf[0];
	for(p = buf + 1;p < buf + BUF_SIZE;p++){//每个值循环输出32遍
		if(*p != ch){
			for(p = buf;p < buf + BUF_SIZE;p++)
				putchar(*p);
			printf("error check \n");
			putchar('\n');
			break;
		}
	}
	pthread_mutex_unlock(&mutex);//解锁,在完成了对共享资源的访问之后,要对互斥量进行解锁
}

void *thread_fun(void *arg){
	int ch = (int)arg;
	for(;;)
		set_buf((char)ch);
}

int main(){
	pthread_t id1;
	int res,ch;
	char ASC[ASC_SIZE];
	int z,i,j;	
	char a;
	char *p;
	for(z=0;z<ASC_SIZE;z++){//输入数据
		scanf("%c",&ASC[z]);
	}
	/*冒泡排序*/
	for(i = 0;i < ASC_SIZE-1;i++){
		for(j = 0;j < ASC_SIZE-i-1;j++){
			if(ASC[j] > ASC[j+1]){
				a = ASC[j];
				ASC[j] = ASC[j+1];
				ASC[j+1] = a;
			}
		}
	}/*
	for(z=0;z<ASC_SIZE;z++){//输出数据
                printf("%c",ASC[z]);
        }
	return 0;*/
	memset(buf,0,BUF_SIZE);/*void *memset(void *s,int c,size_t n)
				总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。
				memset() 函数常用于内存空间初始化。如:
				char str[100];
				memset(str,0,100);*/
	for(p=ASC;p <= ASC + ASC_SIZE;p++){
	//for(ch='A';ch <='F';ch++){
		//printf("%c",*p);
		ch = *p;
//		res = pthread_create(&id1,NULL,thread_fun,(void *)*p);
		res = pthread_create(&id1,NULL,thread_fun,(void *)ch);
		if(res)
			printf("error\n");
			return 1;
		}
	}
	for(;;){
		check();
		sleep(1);
	}
	pthread_join(id1,NULL);
	pthread_mutex_destroy(&mutex);/*销毁锁。锁在是使用完成后,需要进行销毁以释放资源。*/
	return 0;
}

后面执行之后会发现,出现了pthread_create中出现的问题,原因也是一样的,其实,原版不是这个样子的,原版是将A——H八个进程写死,然后输出执行。上面这份code呢,是输入八个乱序字符,然后先排序,之后再进行创建八个进程。其实原理都是一个样子的。然后学长告诉我这样去理解互斥锁的原理,假如有三个将军。分别带着一群小弟,去走同一个门,首先是三个将军同时去抢这个门,谁先抢到,就占领这个门,然后让自己的小弟去依次通过这个门,然后剩下的两个将军继续如上操作。大概这就是最简单的互斥锁原理吧。

后来发现,这个2.0版本貌似也不好用,,,但是也没有看出来哪里有问题。可能是for(;;)这个死循环的原因吧,因此就有了最终版本

/*main.c*/
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define B_SIZE 8
#define A_SIZE 8

void set_buf(char ch){//对线程进行加锁
	char c=ch;
	int n=B_SIZE;
	pthread_mutex_lock(&mutex);/*
					pthread_mutex_lock的作用实际就是上锁,
					这个函数和pthread_mutex_unlock配套使用。
					两句函数中间的代码就是被上锁的代码,
					被上锁的代码只能有一个线程使用,别的线程执行到这里会发生阻塞,
					只有unlock之后,别的线程才能使用lock之后进入代码*/
	while(n--){
		
		putchar(c);
	}
	pthread_mutex_unlock(&mutex);//解锁
}
void *start(void *arg){
	int ch = (int)arg;
	//int n = 2;
//	for(;;)
		set_buf((char)ch);
}

int main(){
	pthread_t id1;
	int res,ch;
	char ASC[A_SIZE];
	int z,i,j;	
	char a;
	char *p;
	for(z=0;z<A_SIZE;z++){//输入数据
		scanf("%c",&ASC[z]);
	}
	int str = strlen(ASC);
	
	getchar();
	//gets(ASC);
	/*冒泡排序*/
	for(i = 0;i < str-1;i++){
		for(j = 0;j < str-i-1;j++){
			if(ASC[j] > ASC[j+1]){
				a = ASC[j];
				ASC[j] = ASC[j+1];
				ASC[j+1] = a;
			}
		}
	}
	for(z=0;z<A_SIZE;z++){//输出数据
                printf("%c",ASC[z]);
        }
	printf("\n");
	for(p=ASC;p <= ASC + str;p++){
		ch = *p;
//		res = pthread_create(&id1,NULL,thread_fun,(void *)*p);
		res = pthread_create(&id1,NULL,start,(void *)ch);
		if(res){
			printf("error\n");
			return 1;
		}
		pthread_join(id1,NULL);
	}
	pthread_mutex_destroy(&mutex);/*销毁锁。锁在是使用完成后,需要进行销毁以释放资源。*/
	return 0;
}

执行结果:

qwertyui
eiqrtuwy
eeeeeeeeiiiiiiiiqqqqqqqqrrrrrrrrttttttttuuuuuuuuwwwwwwwwyyyyyyyy

哎,好用了!


就算我是K歌之王,也不见得把爱情唱的完美