实验一 进程和线程
设计 - 多线程实现单词统计工具
1、实验说明
设有两个文本文件 file1.txt、file2.txt,统计两个文件中单词的总数。
2、解决方案
区分单词原则:凡是一个非字母或数字的字符跟在字母或数字的后面,那么这个字母或数字就是单词的结尾。允许线程使用互斥锁来修改临界资源,确保线程间的同步与协作。如果两个线程需要安全地共享一个公共计数器,需要把公共计数器加锁。线程需要访问称为互斥锁的变量,它可以使线程间很好地合作,避免对于资源的访问冲突。
代码
#include
#include #include
#include
//互斥信号量
pthread_mutexattr_t counter_clock;
//公共计数器
int total_word = 0;
//线程函数,即统计一个文件的单词个数函数
void *count_words(void *f)
{
char *filename = (char *)f;
FILE *fp;
int c;
int prevc = '\0';
if((fp=fopen(filename,"r"))!=NULL)
{
while((c=getc(fp))!=EOF)
{
//isalnum()用来判断一个字符是否为数字或者字母,也就是说判断一个字符是否属于a~z||A~Z||0~9,是返回非0。
if(!isalnum(c)&&isalnum(prevc))
{
pthread_mutex_lock(&counter_clock);
//线程加锁
total_word++;
pthread_mutex_unlock(&counter_clock);
//线程解锁
}
prevc = c;
}
fclose(fp);
}
//打开失败
else
{
perror(filename);
}
return NULL;
}
/*argcargc = argument count :表示传入main函数的数组元素个数,为int类型,
而 argv = argument vector :表示传入main函数的指针数组,为char*类型。
第一个数组元素argv[0]是程序名称,并且包含程序所在的完整路径。argc至少为1,即
argv数组至少包含程序名。*/
//本个程序,由于是两个文件,main函数需要传入两个参数,由于计入本身程序,所以argc=3,int main(int argc,char*argv[])
{
//调试格式,
if(argc!=3)
{
printf("Usage:%s file1 file2\n",argv[0]);
exit(1);
}
//线程t1,t2
pthread_t t1,t2;
int res;
//初始化临界区
res = pthread_mutex_init(&counter_clock,NULL);
if(res!=0)
{
perror("Mutex initialization failed\n");
exit(EXIT_FAILURE);
}
/*int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
创建线程1
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。*/
res = pthread_create(&t1,NULL,count_words,argv[1]);
printf(" Thread 01 creating...\n");
if(res!=0)
{
perror("Thread create failly\n");
exit(EXIT_FAILURE);
}
else
{
printf(" Thread 01 create successfully!\n");
}
//创建线程2
res = pthread_create(&t2,NULL,count_words,argv[2]);
printf(" Thread 02 creating...\n");
if(res!=0)
{
perror("Thread create failly\n");
exit(EXIT_FAILURE);
}
else
{
printf(" Thread 02 create successfully!\n");
}
//让线程1进入等待态
res = pthread_join(t1,NULL);
printf(" Thread 01 joinning...\n");
if(res!=0)
{
perror("Thread join failly\n");
exit(EXIT_FAILURE);
}
else
{
printf(" Thread 01 joined successfully!\n");
}
//让线程2进入等待态
res = pthread_join(t2,NULL);
printf(" Thread 02 joinning...\n");
if(res!=0)
{
perror("Thread join failly\n");
exit(EXIT_FAILURE);
}
else
{
printf(" Thread 02 joined successfully!\n");
}
//输出统计出来的单词总数
printf("There are %d words in two files\n",total_word);
pthread_mutex_destroy(&counter_clock);
return 0;
}
运行结果:
文章图片
文章图片
【实验一 进程和线程】参考链接
1、thread_create函数详解:https://blog.csdn.net/zhou1021jian/article/details/71514738
2、thread_join函数详解:https://blog.csdn.net/yzy1103203312/article/details/80849831
pthread_join()函数原型: int pthread_join(pthread_t thread, void **retval); args: pthread_t thread: 被连接线程的线程号 void **retval: 指向一个指向被连接线程的返回码的指针的指针 return: 线程连接的状态,0是成功,非0是失败
当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前(主)线程才会重新开始执行。当 pthread_join() 函数返回后,被调用线程才算真正意义上的结束,它的内存空间也会被释放(如果被调用线程是非分离的)。这里有三点需要注意:
1、被释放的内存空间仅仅是系统空间,你必须手动清除程序分配的空间,比如 malloc() 分配的空间。
2、 一个线程只能被一个线程所连接。
3、被连接的线程必须是非分离的,否则连接会出错。
所以可以看出pthread_join()有两种作用:
- 用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。
- 对线程的资源进行回收:如果一个线程是非分离的(默认情况下创建的线程都是非分离)并且没有对该线程使用 pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”。
推荐阅读
- 一个人的旅行,三亚
- 一个小故事,我的思考。
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 开学第一天(下)
- 一个人的碎碎念
- 2018年11月19日|2018年11月19日 星期一 亲子日记第144篇
- 遇到一哭二闹三打滚的孩子,怎么办┃山伯教育
- 第326天
- Y房东的后半生14
- 奔向你的城市