标签 Solaris 下的文章

GCC警告选项例解

程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚决不放过。有人会说:我们可以使用比编译器更加严格的静态代码检查工具,如splint。这个建议也很不错。不过lint工具使用起来较繁琐,有时候还需要记住一些特定符号并插入到你自己的代码中才行,门槛较高,这也让很多人止步于此。那么我们就从此放弃么?不,如今的编译器做得都很好,它可以帮助我们的找到绝大多数可能出现问题的代码,前提是你要学会控制编译器去找到这些问题代码,而熟悉编译器的警告选项恰恰是体现控制力的好方法。当你可以自如控制编译器警告输出的时候,你就算是'入道'了,同时你对语言的理解也更进一步了。

有人说:我就是用一个-Wall选项就可以了,一般选手可以这么做,而且他可以不知道-Wall会跟踪哪些类型的问题;但是高级选手是不会只使用-Wall的,他会把每条警告都研究的很透彻,会在Makefile中列出他想让编译器输出哪些类型的警告以替代-Wall,他会屏蔽掉那些对他的代码'毫无用处'的警告(很可能他使用了编译器对语言的扩展功能),他会有个和编译器交流的过程。

俗话说:'工欲善其事,必先利其器',一直在工作中使用GNU C编译器(以下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考。

1. -Wall集合警告选项
我们平时可能大多数情况只使用-Wall编译警告选项,实际上-Wall选项是一系列警告编译选项的集合。下面逐一分析这一集合中的各个选项:

[-Wchar-subscripts]
如果数组使用char类型变量做为下标值的话,则发出警告。因为在某些平台上char可能默认为signed char,一旦溢出,就可能导致某些意外的结果。

e.g.
/* test_signed_char.c */
#include

int main () {
        char    c       = 255; // 我们以为char是无符号的,其范围应该是[0,255]
        int     i       = 0;
        int     a[256];

        for (i = 0; i < 256; i++) {
                a[i] = 1;
        }

        printf("%d\n", c); // 我们期待输出255
        printf("%d\n", a[c][/c][/c]); // 我们期待输出1
        printf("%d\n", a[255]);
        return 0;
}

gcc -Wchar-subscripts test_signed_char.c
test_signed_char.c: In function `main':
test_signed_char.c:13: warning: array subscript has type `char'

其输出结果:
-1
-4197476
1
从输出结果来看Solaris 9/gcc 3.2上char默认实现类型为signed char;在Windows XP/gcc-3.4.2上也是一样。
Windows上的输出结果:
-1
16 (随机值)
1

[-Wcomment]
当'/*'出现在 '/* … */'注释中,或者'\'出现在'// …'注释结尾处时,使用-Wcomment会给出警告。不要小觑这些马虎代码,它很可能会影响程序的运行结果。如下面的例子:

e.g.
/*
 * test_comment.c
 * gcc -Wcomment test_comment.c
 */
#include

int main() {
        int     a       = 1;
        int     b       = 2;
        int     c       = 0; // ok just test\
        c = a + b;

        /*
         * 这里我们期待c = 3
         * /* 但实际上输出c = 0
         */
        printf("the c is %d\n", c);
        return 0;
}

gcc -Wcomment test_comment.c
test_comment.c:10:30: warning: multi-line comment
test_comment.c:15:12: warning: "/*" within comment

输出:
the c is 0

[-Wformat]
检查printf和scanf等格式化输入输出函数的格式字符串与参数类型的匹配情况,如果发现不匹配则发出警告。某些时候格式字符串与参数类型的不匹配会导致程序运行错误,所以这是个很有用的警告选项。

e.g.
/*
 * test_format.c
 */
#include

int main() {
        long    l       = 1;
        double  d       = 55.67;
        printf("%d\n", l);
        printf("%d\n", d);
        return 0;
}

gcc -Wformat test_format.c
test_format.c: In function `main':
test_format.c:10: warning: int format, long int arg (arg 2)
test_format.c:11: warning: int format, double arg (arg 2)

输出:
1
1078711746

[-Wimplicit]
该警告选项实际上是-Wimplicit-int和-Wimplicit-function-declaration两个警告选项的集合。前者在声明函数却未指明函数返回类型时给出警告,后者则是在函数声明前调用该函数时给出警告。

e.g.
/*
 * test_implicit.c
 */
#include

add(int a, int b) { //函数没有声明返回类型
        return a + b;
}

int test() {
        int     a       = 0;
        int     b       = 0;
        int     c       = 0;
        int     d       = 0;

        c = add(a, b);
        d = sub(a, b); //未声明sub的函数原型
        return 0;
}

gcc -Wimplicit -c test_implicit.c
test_implicit.c:7: warning: return type defaults to `int'
test_implicit.c: In function `test':
test_implicit.c:18: warning: implicit declaration of function `sub'

[-Wmissing-braces]
当聚合类型或者数组变量的初始化表达式没有'充分'用括号{}括起时,给出警告。文字表述很难理解,举例说明则清晰些。看下面的例子:

e.g.
/*
 * test_missing_braces.c
 */
struct point {
        int     x;
        int     y;
};

struct line {
        struct point start;
        struct point end;
};

typedef struct line line;

int main() {
        int     array1[2][2]    = {11, 12, 13, 14};
        int     array2[2][2]    = {{11, 12}, {13, 14}}; // ok
        line    l1              = {1, 1, 2, 2};
        line    l2              = {{2, 2}, {3, 3}}; // ok

        return 0;
}

gcc -Wmissing-braces test_missing_braces.c
test_missing_braces.c: In function `main':
test_missing_braces.c:19: warning: missing braces around initializer
test_missing_braces.c:19: warning: (near initialization for `array1[0]')
test_missing_braces.c:21: warning: missing braces around initializer
test_missing_braces.c:21: warning: (near initialization for `l1.start')

[-Wparentheses]
这是一个很有用的警告选项,它能帮助你从那些看起来语法正确但却由于操作符优先级或者代码结构'障眼'而导致错误运行的代码中解脱出来。好长的一个长句,还是看例子理解吧!:)

e.g.
/*
 * test_parentheses.c
 * gcc -Wparentheses  test_parentheses.c
 */
#include

int main() {
        int     a       = 1;
        int     b       = 1;
        int     c       = 1;
        int     d       = 1;

        if (a && b || c) { // 人们很难记住逻辑操作符的操作顺序,所以编译器建议加上()
                ;
       
}

        if (a == 12)
                if (b)
                        d = 9; 
        else
                d = 10; //从代码的缩进上来看,这句仿佛是if (a == 12)的else分支

        printf("the d is %d\n", d); //期待d = 10, 而结果却是1
        return 0;
}

gcc -Wparentheses test_parentheses.c
test_parentheses.c: In function `main':
test_parentheses.c:13: warning: suggest parentheses around && within ||
test_parentheses.c:17: warning: suggest explicit braces to avoid ambiguous `else'

输出:
the d is 1

[-Wsequence-point]
关于顺序点(sequence point),在C标准中有解释,不过很晦涩。我们在平时编码中尽量避免写出与实现相关、受实现影响的代码便是了。而-Wsequence-point选项恰恰可以帮我们这个忙,它可以帮我们查出这样的代码来,并给出其警告。

e.g.
/*
 * test_sequence_point.c
 * gcc -Wsequence-point test_sequence_point.c
 */

#include

int main() {
        int     i = 12;
        i = i–;
        printf("the i is %d\n", i);
        return 0;
}

gcc -Wsequence-point test_sequence_point.c
test_sequence_point.c: In function `main':
test_sequence_point.c:10: warning: operation on `i' may be undefined

在两个平台上给出的编译警告都是一致的,但是输出结果却大相径庭。

Solaris输出:
the i is 11

Windows输出:
the i is 12

类似的像这种与顺序点相关的代码例子有:
i = i++;
a[i] = b[i++]
a[i++] = i
等等…

[-Wswitch]
这个选项的功能浅显易懂,通过文字描述也可以清晰的说明。当以一个枚举类型(enum)作为switch语句的索引时但却没有处理default情况,或者没有处理所有枚举类型定义范围内的情况时,该选项会给处警告。

e.g.
/*
 * test_switch1.c
 */
enum week {
        SUNDAY,
        MONDAY,
        TUESDAY /* only an example , we omitted the others */
};

int test1() {
        enum week       w       = SUNDAY;
        switch(w) {
                case SUNDAY:
                        break; // without default or the other case handlings
        };

        return 0;
}

int test2() { // Ok, won't invoke even a warning
        enum week       w       = SUNDAY;
        switch(w) {
                case SUNDAY:
                        break;
                default:
                        break;               
        };

        return 0;
}

int test3() { // Ok, won't invoke even a warning
        enum week       w       = SUNDAY;
        switch(w) {
                case SUNDAY:
                        break;
                case MONDAY:
                        break;
                case TUESDAY:
                        break;            
        };

        return 0;
}

gcc -Wswitch -c test_switch.c
test_switch.c: In function `test1':
test_switch.c:16: warning: enumeration value `MONDAY' not handled in switch
test_switch.c:16: warning: enumeration value `TUESDAY' not handled in switch

[-Wunused]
-Wunused是-Wunused-function、-Wunused-label、-Wunused-variable、-Wunused-value选项的集合,-Wunused-parameter需单独使用。
(1) -Wunused-function用来警告存在一个未使用的static函数的定义或者存在一个只声明却未定义的static函数,参见下面例子中的func1和func2;
(2) -Wunused-label用来警告存在一个使用了却未定义或者存在一个定义了却未使用的label,参加下面例子中的func3和func7;
(3) -Wunused-variable用来警告存在一个定义了却未使用的局部变量或者非常量static变量;参见下面例子中func5和var1;
(4) -Wunused-value用来警告一个显式计算表达式的结果未被使用;参见下面例子中func6
(5) -Wunused-parameter用来警告一个函数的参数在函数的实现中并未被用到,参见下面例子中func4。

下面是一个综合的例子
e.g.
/*
 * test_unused.c
 */
static void func1(); //to prove function used but never defined
static void func2(); //to prove function defined but not used
static void func3(); //to prove label used but never defined
static void func7(); //to prove label defined but never used
static void func4(int a); //to prove parameter declared but not used
static void func5(); //to prove local variable defined but not used
static void func6(); //to prove value evaluated but not used

static int var1;

void test() {
        func1();
        func3();
        func4(4);
        func5();
        func6();
}

static void func2() {
        ; // do nothing
}

static void func3() {
        goto over;
}

static void func4(int a) {
        ; // do nothing
}

static void func5() {
        int     a = 0;
}

static void func6() {
        int     a = 0;
        int     b = 6;
        a + b;
}

gcc -Wunused-parameter -c test_unused.c //如果不是用-Wunused-parameter,则func4函数将不被警告。
test_unused.c: In function `func3':
test_unused.c:30: label `over' used but not defined
test_unused.c: In function `func7':
test_unused.c:35: warning: deprecated use of label at end of compound statement
test_unused.c:34: warning: label `over' defined but not used
test_unused.c: In function `func4':
test_unused.c:37: warning: unused parameter `a'
test_unused.c: In function `func5':
test_unused.c:42: warning: unused variable `a'
test_unused.c: In function `func6':
test_unused.c:48: warning: statement with no effect
test_unused.c: At top level:
test_unused.c:6: warning: `func1' used but never defined
test_unused.c:25: warning: `func2' defined but not used
test_unused.c:14: warning: `var1' defined but not used

[-Wuninitialized]
该警告选项用于检查一个局部自动变量在使用之前是否已经初始化了或者在一个longjmp调用可能修改一个non-volatile automatic variable时给出警告。目前编译器还不是那么smart,所以对有些可以正确按照程序员的意思运行的代码还是给出警告。而且该警告选项需要和'-O'选项一起使用,否则你得不到任何uinitialized的警告。

e.g.
/*
 * test_uninitialized.c
 */
int test(int y) {
        int     x;

        switch (y) {
                case 1:
                        x = 11;
                        break;
                case 2:
                        x = 22;
                        break;
                case 3:
                        x = 33;
                        break;
        }

        return x;
}

gcc -Wuninitialized -O -c test_uninitialized.c
test_uninitialized.c: In function `test':
test_uninitialized.c:6: warning: `x'
might be used uninitialized in this function

2、非-Wall集合警告选项
以下讨论的这些警告选项并不包含在-Wall中,需要程序员显式添加。

[-Wfloat-equal]
该项用来检查浮点值是否出现在相等比较的表达式中。

e.g.
/*
 * test_float_equal.c
 */

void test(int i) {
        double  d = 1.5;
        if (d == i) {
                ;
        }
}

gcc -Wfloat-equal -c test_float_equal.c
test_float_equal.c: In function `test':
test_float_equal.c:8: warning: comparing floating point with == or != is unsafe

[-Wshadow]
当局部变量遮蔽(shadow)了参数、全局变量或者是其他局部变量时,该警告选项会给我们以警告信息。

e.g.
/*
 * test_shadow.c
 */
int     g;

void test(int i) {
        short   i;
        double  g;
}

gcc -Wshadow -c test_shadow.c
test_shadow.c: In function `test':
test_shadow.c:9: warning: declaration of `i' shadows a parameter
test_shadow.c:10: warning: declaration of `g' shadows a global declaration
test_shadow.c:6: warning: shadowed declaration is here

[-Wbad-function-cast]
当函数(准确地说应该是函数返回类型)被转换为非匹配类型时,均产生警告。

e.g.
/*
 * test_bad_func_case.c
 */
int add(int a, int b) {
        return a+b;
}

void test() {
        char *p = (char*)add(1, 13);
}

gcc -Wbad-function-cast -c test_bad_func_case.c
test_bad_func_case.c: In function `test':
test_bad_func_case.c:11: warning: cast does not match function type

[-Wcast-qual]
当去掉修饰源Target的限定词(如const)时,给出警告。

e.g.
/*
 * test_cast_qual.c
 */
void test() {
        char            c       = 0;
        const char      *p      = &c;
        char            *q;

        q = (char*)p;
}

gcc -Wcast-qual -c test_cast_qual.c
test_cast_qual.c: In function `test':
test_cast_qual.c:10: warning: cast discards qualifiers from pointer target type

[-Wcast-align]
这是个非常有用的选项,特别是对于在Solaris这样的对内存对齐校验的平台尤其重要。它用于在从对齐系数小的地址(如char*)转换为对齐系数大的地址(如int*)转换时给出警告。

e.g.
/*
 * test_cast_align.c
 */
#include
int main() {
        char    c = 1;
        char    *p = &c; //ok
        int     *q = (int*)p; //bad align-cast
        printf("the *q is %d\n", *q);
        return 0;
}

gcc -Wcast-align test_cast_align.c
test_cast_align.c: In function `main':
test_cast_align.c:9: warning: cast increases required alignment of target type

输出:
总线错误 ((主存储器)信息转储) //on Solaris 9

[-Wsign-compare]
在有符号数和无符号数进行值比较时,有符号数可能在比较之前被转换为无符号数而导致结果错误。使用该选项会对这样的情况给出警告。

e.g.
/*
 * test_sign_compare.c
 */
#include

int main() {
        unsigned int    i = 128;
        signed int      j = -1;
        if (i < j) {
                printf("i < j\n");
        } else {
                printf("i > j\n");
        }
        return 0;
}

gcc -Wsign-compare test_sign_compare.c
test_sign_compare.c: In function `main':
test_sign_compare.c:10: warning: comparison between signed and unsigned

输出:
i < j

[-Waggregate-return]
如果一个函数返回一个聚合类型,如结构体、联合或者数组,该选项就会给出警告信息。较简单不举例了。

[-Wmultichar]
当我们写下如此代码时:char c = 'peter', 使用该选项会给出警告。这个选项是默认选项,你无需单独使用该选项,不过你可以使用-Wno-multichar来关闭这些警告信息,但是这可是不建议你去做的。对于char c = 'peter'这样的代码的处理是与平台相关,不可移植的。

e.g.
/*
 * test_multichar.c
 */
int main() {
        char c = 'peter';
        printf("c is %c\n", c);
        return 0;
}
但这里在Windows和Solaris平台输出的结果却一致:
c is r

[-Wunreachable-code]
这个选项是一个检查冗余代码或疏忽代码好办法。它一旦检查到你的代码中有不可达的代码,就会发出警告。这些代码往往会存在潜在的危机。

e.g.
/*
 * test_unreachable.c
 */
int test(char c) {
        if (c < 256) {
                return 0;
        } else {
                return 1;
        }
}

gcc -Wunreachable-code -c test_unreachable.c
test_unreachable.c: In function `test':
test_unreachable.c:6: warning: comparison is always true due to limited range of data type
test_unreachable.c:9: warning: will never be executed

[-Wconvertion]
由于原型定义而引起的定点和浮点数之间的隐式转换(强制转换)或者由有符号数和无符号数之间隐式转换转换引起的警告。

e.g.
/*
 * test_conversion.c
 */
#include

void getdouble(double d) {
        ;       // do nothing
}

int main() {
        unsigned int    k;
        int             n       = 12;

        k = -1;
        k = (unsigned int)-1; // ok, explicit conversion ,no warning

        getdouble(n);
        return 0;
}

gcc -Wconversion test_conversion.c
test_conversion.c: In function `main':
test_conversion.c:15: warning: negative integer implicitly converted to unsigned type
test_conversion.c:18: warning: passing arg 1 of `getdouble' as floating rather than integer due to prototype

3、-Wtraditional和-W
这两个警告选项其实也都是一些组合(大部分都在上面提到过),前者用来在代码中使用了标准C不同于传统C的特性时,发出警告;后者也是针对一些事件打开一个警告集合。关于它们的说明具体可参见'Using the GNU Compiler Collection'。

[注1]
本文中的例子的测试环境为Solaris 9 SPARC平台,GCC-3.2和Windows XP Intel x86平台,mingw32 gcc3.4.2,如无特殊差异,所有注释均针对这两个测试环境。

APR源代码分析-线程同步篇

在线程同步方面,Posix标准定义了3种同步模型,分别为互斥量条件变量读写锁。APR也“浅”封装了这3种模型,只是在“读写锁”一块儿还没有全部完成。

线程同步的源代码的位置在$(APR_HOME)/locks目录下,本篇blog着重分析unix子目录下的thread_mutex.c、thread_rwlock.c和thread_cond.c文件的内容,其相应头文件为(APR_HOME)/include/apr_thread_mutex.h、apr_thread_rwlock.h和apr_thread_cond.h。

由于APR的封装过于“浅显”,实际上也并没有多少值得分析的“靓点”。所以本篇实际上是在讨论线程同步的3种运行模型。

一、互斥量
互斥量是线程同步中最基本的同步方式。互斥量用于保护代码中的临界区,以保证在任一时刻只有一个线程或进程访问临界区。

1、互斥量的初始化
在POSIX Thread中提供两种互斥量的初始化方式,如下:
(1) 静态初始化
互斥量首先是一个变量,Pthread提供预定义的值来支持互斥量的静态初始化。举例如下:
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
既然是静态初始化,那么必然要求上面的mutex变量需要静态分配。在APR中并不支持apr_thread_mutex_t的使用预定值的静态初始化(但可以变通的利用下面的方式进行静态分配的mutex的初始化)。

(2) 动态初始化
除了上面的情况,如果mutex变量在堆上或在共享内存中分配的话,我们就需要调用一个初始化函数来动态初始化该变量了。在Pthread中的对应接口为pthread_mutex_init。APR封装了这一接口,我们可以使用下面方式在APR中初始化一个apr_thread_mutex_t变量。
        apr_thread_mutex_t *mutex = NULL;
        apr_pool_t  *pool = NULL;
        apr_status_t  stat;

        stat = apr_pool_create(&pool, NULL);
        if (stat != APR_SUCCESS) {
                printf("error in pool %d\n", stat);
        } else {
                printf("ok in pool\n");
        }

        stat = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, pool);
        if (stat != APR_SUCCESS) {
                printf("error %d in mutex\n", stat);
        } else {
                printf("ok in mutex\n");
        }

2、互斥锁的软弱性所在
互斥锁之软弱性在于其是一种协作性锁,其运作时对各线程有一定的要求,即“所有要访问临界区的线程必须首先获取这个互斥锁,离开临界区后释放该锁”,一旦某一线程不遵循该要求,那么这个互斥锁就形同虚设了。如下面的例子:
举例:我们有两个线程,一个线程A遵循要求,每次访问临界区均先获取锁,然后将临界区的变量x按偶数值递增,另一个线程B不遵循要求直接修改x值,这样即使在线程A获取锁的情况下仍能修改临界区的变量x。

static apr_thread_mutex_t       *mutex  = NULL;
static int                                x       = 0;
static apr_thread_t             *t1     = NULL;
static apr_thread_t             *t2     = NULL;

static void * APR_THREAD_FUNC thread_func1(apr_thread_t *thd, void *data)
{
        apr_time_t      now;
        apr_time_exp_t  xt;

        while (1) {
                apr_thread_mutex_lock(mutex);
                now = apr_time_now();
                apr_time_exp_lt(&xt, now);
                printf("[threadA]: own the lock, time[%02d:%02d:%02d]\n", xt.tm_hour, xt.tm_min,
                         xt.tm_sec);
                printf("[threadA]: x = %d\n", x);
                if (x % 2 || x == 0) {
                        x += 2;
                } else {
                        printf("[threadA]: Warning: x变量值被破坏,现重新修正之\n");
                        x += 1;
                }
                apr_thread_mutex_unlock(mutex);
                now = apr_time_now();
                apr_time_exp_lt(&xt, now);
                printf("[threadA]: release the lock, time[%02d:%02d:%02d]\n", xt.tm_hour, xt.tm_min,
                         xt.tm_sec);
                sleep(2);
        }

        return NULL;
}

static void * APR_THREAD_FUNC thread_func2(apr_thread_t *thd, void *data)
{
        apr_time_t      now;
        apr_time_exp_t  xt;
        while (1) {
                x ++;
                now = apr_time_now();
                apr_time_exp_lt(&xt, now);
                printf("[threadB]: modify the var, time[%02d:%02d:%02d]\n", xt.tm_hour, xt.tm_min,  xt.tm_sec);
                sleep(2);
        }

        return NULL;
}

int main(int argc, const char * const * argv, const char * const *env)
{
        apr_app_initialize(&argc, &argv, &env);
        apr_status_t stat;

        //…

        /*
         * 创建线程
         */
        stat = apr_thread_create(&t1, NULL, thread_func1, NULL, pool);
        stat = apr_thread_create(&t2, NULL, thread_func2, NULL, pool);

         //…

        apr_terminate();
        return 0;
}
//output
… …
[threadA]: own the lock, time[10:10:15]
[threadB]: modify the var, time[10:10:15]
[threadA]: x = 10
[threadA]: Warning: x变量值被破坏,现重新修正之
[threadA]: release the lock, time[10:10:15]
当然这个例子不一定很精确的表明threadB在threadA拥有互斥量的时候修改了x值。

二、条件变量
互斥量一般用于被设计被短时间持有的锁,一旦我们不能确定等待输入的时间时,我们可以使用条件变量来完成同步。我们曾经说过I/O复用,在我们调用poll或者select的时候实际上就是在内核与用户进程之间达成了一个协议,即当某个I/O描述符事件发生的时候内核通知用户进程并且将处于挂起状态的用户进程唤醒。而这里我们所说的条件变量让对等的线程间达成协议,即“某一线程发现某一条件满足时必须发信号给阻塞在该条件上的线程,将后者唤醒”。这样我们就有了两种角色的线程,分别为
(1) 给条件变量发送信号的线程
其流程大致为:
{
        获取条件变量关联锁;
        修改条件为真;
        调用apr_thread_cond_signal通知阻塞线程条件满足了;—— (a)
        释放变量关联锁;
}
(2) 在条件变量上等待的线程
其流程大致为:
{
        获取条件变量关联锁;
        while (条件为假) { ——————— (c)
                调用apr_thread_cond_wait阻塞在条件变量上等待;—— (b)
        }
        修改条件;
        释放变量关联锁;
}
上面两个流程中,理解三点最关键:
a) apr_thread_cond_signal中调用的pthread_cond_signal保证至少有一个阻塞在条件变量上的线程恢复;在《Unix网络编程 Vol2》中也谈过这里存在着一个race。即在发送cond信号的同时,该发送线程仍然持有条件变量关联锁,那么那个恢复线程的apr_thread_cond_wait返回时仍然拿不到这把锁就会再次挂起。这里的这个race要看各个平台实现是如何处理的了。
b) apr_thread_cond_wait中调用的pthread_cond_wait原子的将调用线程挂起,并释放其持有的条件变量关联锁;
c) 这里之所以使用while反复测试条件,是防止“伪唤醒”的存在,即条件并未满足就被唤醒。所以无论怎样,唤醒后我都需要重新测试一下条件,保证该条件的的确确满足了。

条件变量在解决“生产者-消费者”问题中有很好的应用,在我以前的一篇blog中也说过这个问题。

三、读写锁
前面说过,互斥量把想进入临界区而又试图获取互斥量的所有线程都阻塞住了。读写锁则改进了互斥量的这种霸道行为,它区分读临界区数据和修改临界区数据两种情况。这样如果有线程持有读锁的话,这时再有线程想读临界区的数据也是可以再获取读锁的。读锁和写锁的分配规则在《Unix网络编程 Vol2》中有详细说明,这里不详述。

四、小结
三种同步方式如何选择?场合不同选择也不同。互斥量在于完全同步的临界区访问;条件变量在解决“生产者-消费者”模型问题上有独到之处;读写锁则在区分对临界区读写的时候使用。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats