ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

TLPI 第30 章 练习:Threads: Thread Synchronization

2026/8/12 18:52:18 拓冰建站 浏览量
TLPI 第30 章 练习:Threads: Thread Synchronization

笔记和练习博客总目录见:开始读TLPI。

30-1

修改清单 30-1(thread_incr.c)中的程序,使得线程的 start 函数中的每个循环都输出 glob 的当前值以及某个唯一标识线程的标识符。线程的唯一标识符可以作为参数传递给创建线程的 pthread_create() 调用。对于这个程序,这意味着需要将线程 start 函数的参数改为指向包含唯一标识符和循环上限值的结构体的指针。运行程序,将输出重定向到文件,然后检查文件,看看当内核调度程序在两个线程之间交替执行时,glob 会发生什么变化。


清单 30-1为threads/thread_incr.c。

代码如下:

#include<pthread.h>#include"tlpi_hdr.h"staticvolatileintglob=0;/* "volatile" prevents compiler optimizations of arithmetic operations on 'glob' */typedefstruct{inttid;intloops;}Thread_args;staticvoid*/* Loop arg->loops times incrementing 'glob' */threadFunc(void*arg){Thread_args*targ=(Thread_args*)arg;intloc,j;for(j=0;j<targ->loops;j++){loc=glob;loc++;glob=loc;printf("t%d:%d\n",targ->tid,glob);}returnNULL;}intmain(intargc,char*argv[]){pthread_tt1,t2;Thread_args targ1,targ2;ints;targ1.loops=targ2.loops=(argc>1)?getInt(argv[1],GN_GT_0,"num-loops"):10000000;targ1.tid=1;targ2.tid=2;s=pthread_create(&t1,NULL,threadFunc,&targ1);if(s!=0)errExitEN(s,"pthread_create");s=pthread_create(&t2,NULL,threadFunc,&targ2);if(s!=0)errExitEN(s,"pthread_create");s=pthread_join(t1,NULL);if(s!=0)errExitEN(s,"pthread_join");s=pthread_join(t2,NULL);if(s!=0)errExitEN(s,"pthread_join");printf("glob = %d\n",glob);exit(EXIT_SUCCESS);}

运行如下,输出到日志文件out:

$ ./ex30-11000000>out $tail-fout t1:1998633 t1:1998634 t1:1998635 t1:1998636 t1:1998637 t1:1998638 t1:1998639 t1:1998640 t1:1998641 glob=1998641

由于没到2000000,说明日志文件out中肯定有重复的值,即t1:xxxx和t2:xxxx。

找出重复值:

$sed's/^...//'out|sort|uniq-D>out.repeat $wc-lout.repeat2720out.repeat

查看重复值,说明当数字很大时才会出现问题:

$moreout.repeat10001801000180100577110057711006209100620910077181007718...

对应日志文件out中的位置:

1000916行:t2:1000180...## 一直是t2的输出1000935行:t2:10001991000936行:t1:1000180......1006508行:t1:10057711006509行:t2:10057711006510行:t2:1005773## 日志文件中无t2:1005772......1006947行:t1:10062091006948行:t2:10062091006949行:t2:1006211## 日志文件中无t2:1006210

30-2

实现一组线程安全的函数,用于更新和搜索非平衡二叉树。这个库应该包括以下形式的函数(用途显而易见):

initialize(tree);add(tree,char*key,void*value);delete(tree,char*key)Booleanlookup(char*key,void**value)

在上面的原型中,树是一个指向树根的结构(你需要为此目的定义一个合适的结构)。树的每个元素都保存一个键值对。你还需要为每个元素定义一个结构,包括一个互斥锁,用来保护该元素,以确保同一时间只有一个线程可以访问它。initialize()、add() 和 lookup() 函数实现起来比较简单。delete() 操作则需要花费更多的精力。

不需要维护平衡树大大简化了实现的锁定需求,但也有风险,某些输入模式可能会导致树的性能很差。维护平衡树则需要在 add() 和 delete() 操作中在子树之间移动节点,这就需要更复杂的锁定策略。


此题暂无时间,先略过了。