好文档 - 专业文书写作范文服务资料分享网站

跟我学Linux编程-13-多线程编程-性能

天下 分享 时间: 加入收藏 我要投稿 点赞

多线程编程-性能

在上一章节中,我们介绍了线程互斥锁的使用。通过互斥锁,使得每个线程只能串行地运行临界区代码,从而有效地避免了多线程冲突。串行的代码运行方式削弱了多线程并发运行的特性,因此线程锁也潜在地降低了程序性能,如何杜绝线程冲突,又尽可能不影响程序效率,是我们每一个线程从员需要认真考虑的事情。

减少线程性能下降的方法有如下几点: 1 尽可能减小互斥锁的颗粒,使串行代码的比例减小从而提高效率,这在上一章节末已提过。 2 加锁解锁之间的代码,运行时间尽可能减少,避免有sleep或死循环一类的超时等待。 3 对不同的冲突资源使用不同的线程锁,避免不相关的线线之间因锁反而产生关联。 4 使用pthread_mutex_trylock代替pthread_lock在检测和处理锁冲突,实现单线程对多个对像的非阻塞处理。

今天我们重点介绍pthread_mutex_trylock的使用,并通过实例的来展现其用法,探究其提高程序效果的原理。

首先,我们来看pthread_lock与pthread_mutex_trylock的函数原型: int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock( pthread_mutex_t *mutex);

可以看到,这两个函数的原型非常想像,功能也比较类似,都是尝试对给定的锁对像进行加锁,如果成功,则线程获得该锁,并返回0;不同点在于当锁已被其他线程占有的情况下,pthread_mutex_lock会阻塞,直至锁被其它线程释放并且本线程获得锁;如pthread_mutex_trylock则不然,如果锁当前已被其他线程占有,则立刻返回失败(非0值),我们的程序可判读返回值并进行下一步的处理(如处理另一个任务),避免线程被阻塞,从而提高了线程并发度,提升程序性能。我们接下来看例子:

#include #include #include

typedef struct { int m_cnt[3]; pthread_mutex_t m_mutex; } count_t;

#define COUNT_CNT 20 #define CYC_CNT 10000

count_t g_counts[COUNT_CNT];

void *thread_task1(void *arg)

{

int i;

int cnt = 0;

//保证每个count_t对像的m_cnt[0]值都被累加到指定值CYC_CNT while (cnt < COUNT_CNT) { //依次处理每一个count_t对像 for (i = 0; i < COUNT_CNT; i++) { count_t *c = g_counts + i;

//当cnt成员累加到CYC_CNT值时,不再对其进行处理 if (c->m_cnt[0] >= CYC_CNT) { if (c->m_cnt[0] == CYC_CNT) cnt++; //标识已处理完一个 continue; }

//尝试加锁

if (pthread_mutex_trylock(&c->m_mutex)) {

//获取锁失败,跳过处理下一个对像 continue; } c->m_cnt[0]++; pthread_mutex_unlock(&c->m_mutex); } usleep(10); } printf(\ return NULL; }

void *thread_task2(void *arg) { int i; int cnt = 0; //保证每个count_t对像的m_cnt[1]值都被累加到指定值CYC_CNT while (cnt < COUNT_CNT)

{ //依次处理每一个count_t对像 for (i = 0; i < COUNT_CNT; i++) { count_t *c = g_counts + i; //当cnt成员累加到CYC_CNT值时,不再对其进行处理 if (c->m_cnt[1] >= CYC_CNT) { if (c->m_cnt[1] == CYC_CNT) cnt++; continue;

}

//加锁 pthread_mutex_lock(&c->m_mutex); c->m_cnt[1]++; pthread_mutex_unlock(&c->m_mutex); } usleep(10); } printf(\ return NULL; }

void count_proc(count_t *c) { //模拟一个比较消耗时间的处理操作 c->m_cnt[2] = c->m_cnt[0] * c->m_cnt[1]; usleep(1000); }

void main_thread_task() { int i; int cnt = 0; while (cnt++ < 100) { for (i = 0; i < COUNT_CNT; i++) { count_t *c = g_counts + i;

//标识已处理完一个

pthread_mutex_lock(&c->m_mutex); count_proc(c); pthread_mutex_unlock(&c->m_mutex); } sleep(10); } }

int main(int argc, char *argv[]) { int i; pthread_t thr; }

//初始化变量

for (i = 0; i < COUNT_CNT; i++) { count_t *c = g_counts + i; c->m_cnt[0] = 0; c->m_cnt[1] = 0; c->m_cnt[2] = 0;

pthread_mutex_init(&c->m_mutex, NULL); //线程锁一定要初始化 }

//创建两个不同的线程

pthread_create(&thr, NULL, thread_task1, NULL); pthread_create(&thr, NULL, thread_task2, NULL); //创建另一个thread_task1线程做对比

pthread_create(&thr, NULL, thread_task1, NULL);

//主线程循环

main_thread_task();

return 0;

为了展示pthread_mutex_lock与pthread_mutex_trylock的区别,我们写了两个线程执行函数:thread_task1与thread_task2,这两个线程要完成的工作一样,都是处理COUNT_CNT个count_t对像,将每个count_t的计数器m_cnt累加至CYC_CNT,任务完成后,在屏幕上打印一行信息提示我们使命完成,然后线程退出。不同点在于,thread_task1加锁使用的是pthead_mutex_trylock而thread_task2为pthread_mutex_lock。为了制造冲突,我们在主程序中执行了main_thread_task()循环,该循环中,依次对每个count_t对像进行加锁解锁,并在加锁解锁之间使用usleep()函数模拟了一个耗时操作(注意,这里是为了模块耗时操作,在实际编程程中,加锁与解锁之间使用sleep一类的操作,就是自己找麻烦,这在文章的一开头就说过),加大锁冲突的概率。

在主线程中,我们为thread_task1与thread_task2各创建了一个线程,代码如下:

pthread_create(&thr, NULL, thread_task1, NULL); pthread_create(&thr, NULL, thread_task2, NULL);

如果两个线程的效率相近,则其标识结束的打印信息应该在同一时间输出(当然,总会有先后,但间隔足够小,多次运行可能这次是你先输出,再另一次是我先输出),如果效率差别较大,则输出结束信息会有一个较为固定的先后顺序,总是效率高的在前输出,效率低的在后输出。

通过理论分析,我们预期thread_task1会先结束,因此运行程序的结果总是thread_task1先输出,thread_task2后输出。但认真的同学可能认为这个结果不能说明什么原因,因为thread_task1先创建,而thread_task2后创建,这还有个时间差呢,说不定输出信息的先后就是因为这个时间差导致的,与线程效率没有半分钱关系。考虑到这点,我们上两行程序的后面,再用thread_task1创建了第3个线程,代码如下:

//创建两个不同的线程 pthread_create(&thr, NULL, thread_task1, NULL); pthread_create(&thr, NULL, thread_task2, NULL); //创建另一个thread_task1线程做对比 pthread_create(&thr, NULL, thread_task1, NULL);

如果程序每次都运行结果都是输出两行thread_task1的信息,最后再输出thread_task2的信息,则结论非常有说服力了,时差根本不是问题,thread_task1与效率就是比thread_task2的高。

那我们来编译和执行程序吧: gcc thread4.c -o thread4 –lpthread ./thread4 task 1 done. task 1 done. task 2 done.

结果果然如我们预期。输出信息中前两行是task1,最后一行是task2,这充分说明了使用pthread_mutex_trylock比pthread_mutex_lock的性能要好。为什么呢,我们来分析一下他们的代码差异: thread_task1: //尝试加锁 if (pthread_mutex_trylock(&c->m_mutex)) { //获取锁失败,跳过处理下一个对像 continue; }

跟我学Linux编程-13-多线程编程-性能

多线程编程-性能在上一章节中,我们介绍了线程互斥锁的使用。通过互斥锁,使得每个线程只能串行地运行临界区代码,从而有效地避免了多线程冲突。串行的代码运行方式削弱了多线程并发运行的特性,因此线程锁也潜在地降低了程序性能,如何杜绝线程冲突,又尽可能不影响程序效率,是我们每一个线程从员需要认真考虑的事情。减少线程性能下降的方法有如下几点:1尽可能减小互斥锁的颗
推荐度:
点击下载文档文档为doc格式
2cpfo6fbdd9da6a52izw
领取福利

微信扫码领取福利

微信扫码分享