前面shared_ptr线程安全性问题说到,即使shared_ptr的实现,数据结构丢掉高速缓冲的_M_ptr副本,只留下单个_M_refcount成员,这样做swap仍然是线程不安全的。现在来详细考察一番。
要验是否证线程安全,简单的办法是,做2个线程,各自运行swap语句数十万次,在并发环境下观察运行结果,如果出现非预期的结果,即判定线程不安全。
因为这种方法具有随机性,因此还不能完全令人满意。现在考虑另一种方法,模拟一个中间代码,用解释程序对2个swap语句串扰执行的每一种情况都执行一遍,直接看结果,这样就完全清楚了。
用例swap语句的2个代码是:
swap(a, b):
(0) t1=a;
(1) a=b;
(2) b=t1;
swap(a, c):
(3) t2=a;
(4) a=c;
(5) c=t2;
代码可以用赋值操作的三元式表示。因为操作符都是赋值,因此可以简化:
struct assign {
int *left;
int *right;
};
这个就是中间代码的指令了,相当于三元式(=, [left], [right])。
现在要求出swap的这3个语句串扰执行总共有多少种可能。这可以看作在6个位置的指令片中,任选3个位置,依次填入swap(a, b)的这3个语句,剩下的空位,依次填入swap(a, c)的这3个语句。因此有C(6,3)=20种可能。现在把它都找出来:
struct ins_no {
int s1[3];
int s2[3];
};
struct ins_no all_no[20];
void calc_all_no()
{
int i, j, k;
int m=0;
for(i=0;i<=3; i++)
for(j=i+1; j<6-1; j++)
for(k=j+1; k<6;k++) {
all_no[m].s1[0]=i;
all_no[m].s1[1]=j;
all_no[m].s1[2]=k;
m++;
}
for(m=0; m<20; m++) {
all_no[m].s2[0]= all_no[19-m].s1[0];
all_no[m].s2[1]= all_no[19-m].s1[1];
all_no[m].s2[2]= all_no[19-m].s1[2];
}
}
然后转化成20个指令串。指令需要和操作数绑定:
struct vars {
int a;
int b;
int c;
int t1;
int t2;
};
struct vars init= {'A', 'B', 'C', 'x', 'z'};
struct vars result;
struct assign all_assigns[] = {
{&result.t1,&result.a}, {&result.a, &result.b}, {&result.b,&result.t1},
{&result.t2,&result.a}, {&result.a, &result.c}, {&result.c,&result.t2},
};
void calc_all_ins()
{
int m=0;
int i=0;
int j;
calc_all_no();
for(m=0; m<20; m++){
for(i=0; i<3; i++) {
j=all_no[m].s1[i];
all_ins[m][j]=&all_assigns[i];
}
for(i=0; i<3; i++) {
j=all_no[m].s2[i];
all_ins[m][j]=&all_assigns[i+3];
}
}
}
指令串的每一个指令都是对初始化好的swap(a, b):和swap(a, c):的6个指令的引用。操作数给出初始值{a(‘A’), b(‘B’), c(‘C’), t1(‘x’), t2(‘z’)}。
解释执行就是把安排好的指令流的每个赋值语句操作一遍:
void execute(struct assign **a, int n)
{
int i;
result=init;
printf("\t");
print_vars(&init); printf(" ->\n");
for(i=0; i<n; i++) {
print_ins_nl(a[i], 0);
*(a[i]->left) = *(a[i]->right);
printf("\t");
print_vars(&result); printf("\n");
}
printf("\t");
print_vars(&init); printf(" -> ");
print_vars(&result); printf("\n");
}
这样执行就能看结果了。下面贴上完整的程序和结果:
#include <stdio.h>
struct ins_no {
int s1[3];
int s2[3];
};
struct ins_no all_no[20];
struct vars {
int a;
int b;
int c;
int t1;
int t2;
};
struct vars init= {'A', 'B', 'C', 'x', 'z'};
struct vars result;
struct assign {
int *left;
int *right;
};
struct assign all_assigns[] = {
{&result.t1,&result.a}, {&result.a, &result.b}, {&result.b,&result.t1},
{&result.t2,&result.a}, {&result.a, &result.c}, {&result.c,&result.t2},
};
struct assign *all_ins[20][6];
void print_ins_nl(struct assign *a, int nl)
{
int i;
int *pi= a->left;
printf("\t(%d)\t", a- &all_assigns[0]);
for(i=0; i<2; i++) {
if(pi==&result.a) printf("a"); else
if(pi==&result.b) printf("b"); else
if(pi==&result.c) printf("c"); else
if(pi==&result.t1) printf("t1"); else
if(pi==&result.t2) printf("t2");
if(pi==a->left) {
printf("=");
pi=a->right;
}
}
if(nl)
printf(";\n");
else
printf(";");
}
void print_ins(struct assign *a)
{
print_ins_nl(a, 1);
}
void calc_all_no()
{
int i, j, k;
int m=0;
for(i=0;i<=3; i++)
for(j=i+1; j<6-1; j++)
for(k=j+1; k<6;k++) {
all_no[m].s1[0]=i;
all_no[m].s1[1]=j;
all_no[m].s1[2]=k;
m++;
}
for(m=0; m<20; m++) {
all_no[m].s2[0]= all_no[19-m].s1[0];
all_no[m].s2[1]= all_no[19-m].s1[1];
all_no[m].s2[2]= all_no[19-m].s1[2];
}
}
void calc_all_ins()
{
int m=0;
int i=0;
int j;
calc_all_no();
for(m=0; m<20; m++){
for(i=0; i<3; i++) {
j=all_no[m].s1[i];
all_ins[m][j]=&all_assigns[i];
}
for(i=0; i<3; i++) {
j=all_no[m].s2[i];
all_ins[m][j]=&all_assigns[i+3];
}
}
}
void print_vars(struct vars *var)
{
printf("(%c, %c, %c, :%c, :%c)", var->a, var->b,var->c, var->t1, var->t2);
}
void execute(struct assign **a, int n)
{
int i;
result=init;
printf("\t");
print_vars(&init); printf(" ->\n");
for(i=0; i<n; i++) {
print_ins_nl(a[i], 0);
*(a[i]->left) = *(a[i]->right);
printf("\t");
print_vars(&result); printf("\n");
}
printf("\t");
print_vars(&init); printf(" -> ");
print_vars(&result); printf("\n");
}
int main()
{
int i;
printf("swap(a, b):\n");
for(i=0; i<3; i++) {
print_ins(&all_assigns[i]);
}
printf("swap(a, c):\n");
for(i=0; i<3; i++) {
print_ins(&all_assigns[i+3]);
}
calc_all_ins();
printf("[exec]:\n");
for(i=0; i<20; i++) {
printf("%d:\n", i);
execute(all_ins[i], 6);
}
}
运行结果表明2个swap语句在不加锁的情况下,执行是线程不安全的。所以实际代码需要加锁把它变成原子操作:
swap(a, b):
(0) t1=a;
(1) a=b;
(2) b=t1;
swap(a, c):
(3) t2=a;
(4) a=c;
(5) c=t2;
[exec]:
0:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(1) a=b; (B, B, C, :A, :z)
(2) b=t1; (B, A, C, :A, :z)
(3) t2=a; (B, A, C, :A, :B)
(4) a=c; (C, A, C, :A, :B)
(5) c=t2; (C, A, B, :A, :B)
(A, B, C, :x, :z) -> (C, A, B, :A, :B)
1:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(1) a=b; (B, B, C, :A, :z)
(3) t2=a; (B, B, C, :A, :B)
(2) b=t1; (B, A, C, :A, :B)
(4) a=c; (C, A, C, :A, :B)
(5) c=t2; (C, A, B, :A, :B)
(A, B, C, :x, :z) -> (C, A, B, :A, :B)
2:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(1) a=b; (B, B, C, :A, :z)
(3) t2=a; (B, B, C, :A, :B)
(4) a=c; (C, B, C, :A, :B)
(2) b=t1; (C, A, C, :A, :B)
(5) c=t2; (C, A, B, :A, :B)
(A, B, C, :x, :z) -> (C, A, B, :A, :B)
3:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(1) a=b; (B, B, C, :A, :z)
(3) t2=a; (B, B, C, :A, :B)
(4) a=c; (C, B, C, :A, :B)
(5) c=t2; (C, B, B, :A, :B)
(2) b=t1; (C, A, B, :A, :B)
(A, B, C, :x, :z) -> (C, A, B, :A, :B)
4:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(3) t2=a; (A, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(2) b=t1; (B, A, C, :A, :A)
(4) a=c; (C, A, C, :A, :A)
(5) c=t2; (C, A, A, :A, :A)
(A, B, C, :x, :z) -> (C, A, A, :A, :A)
5:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(3) t2=a; (A, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(2) b=t1; (C, A, C, :A, :A)
(5) c=t2; (C, A, A, :A, :A)
(A, B, C, :x, :z) -> (C, A, A, :A, :A)
6:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(3) t2=a; (A, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(5) c=t2; (C, B, A, :A, :A)
(2) b=t1; (C, A, A, :A, :A)
(A, B, C, :x, :z) -> (C, A, A, :A, :A)
7:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(3) t2=a; (A, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(2) b=t1; (B, A, C, :A, :A)
(5) c=t2; (B, A, A, :A, :A)
(A, B, C, :x, :z) -> (B, A, A, :A, :A)
8:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(3) t2=a; (A, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(5) c=t2; (B, B, A, :A, :A)
(2) b=t1; (B, A, A, :A, :A)
(A, B, C, :x, :z) -> (B, A, A, :A, :A)
9:
(A, B, C, :x, :z) ->
(0) t1=a; (A, B, C, :A, :z)
(3) t2=a; (A, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(5) c=t2; (C, B, A, :A, :A)
(1) a=b; (B, B, A, :A, :A)
(2) b=t1; (B, A, A, :A, :A)
(A, B, C, :x, :z) -> (B, A, A, :A, :A)
10:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(0) t1=a; (A, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(2) b=t1; (B, A, C, :A, :A)
(4) a=c; (C, A, C, :A, :A)
(5) c=t2; (C, A, A, :A, :A)
(A, B, C, :x, :z) -> (C, A, A, :A, :A)
11:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(0) t1=a; (A, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(2) b=t1; (C, A, C, :A, :A)
(5) c=t2; (C, A, A, :A, :A)
(A, B, C, :x, :z) -> (C, A, A, :A, :A)
12:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(0) t1=a; (A, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(5) c=t2; (C, B, A, :A, :A)
(2) b=t1; (C, A, A, :A, :A)
(A, B, C, :x, :z) -> (C, A, A, :A, :A)
13:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(0) t1=a; (A, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(2) b=t1; (B, A, C, :A, :A)
(5) c=t2; (B, A, A, :A, :A)
(A, B, C, :x, :z) -> (B, A, A, :A, :A)
14:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(0) t1=a; (A, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(1) a=b; (B, B, C, :A, :A)
(5) c=t2; (B, B, A, :A, :A)
(2) b=t1; (B, A, A, :A, :A)
(A, B, C, :x, :z) -> (B, A, A, :A, :A)
15:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(0) t1=a; (A, B, C, :A, :A)
(4) a=c; (C, B, C, :A, :A)
(5) c=t2; (C, B, A, :A, :A)
(1) a=b; (B, B, A, :A, :A)
(2) b=t1; (B, A, A, :A, :A)
(A, B, C, :x, :z) -> (B, A, A, :A, :A)
16:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(4) a=c; (C, B, C, :x, :A)
(0) t1=a; (C, B, C, :C, :A)
(1) a=b; (B, B, C, :C, :A)
(2) b=t1; (B, C, C, :C, :A)
(5) c=t2; (B, C, A, :C, :A)
(A, B, C, :x, :z) -> (B, C, A, :C, :A)
17:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(4) a=c; (C, B, C, :x, :A)
(0) t1=a; (C, B, C, :C, :A)
(1) a=b; (B, B, C, :C, :A)
(5) c=t2; (B, B, A, :C, :A)
(2) b=t1; (B, C, A, :C, :A)
(A, B, C, :x, :z) -> (B, C, A, :C, :A)
18:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(4) a=c; (C, B, C, :x, :A)
(0) t1=a; (C, B, C, :C, :A)
(5) c=t2; (C, B, A, :C, :A)
(1) a=b; (B, B, A, :C, :A)
(2) b=t1; (B, C, A, :C, :A)
(A, B, C, :x, :z) -> (B, C, A, :C, :A)
19:
(A, B, C, :x, :z) ->
(3) t2=a; (A, B, C, :x, :A)
(4) a=c; (C, B, C, :x, :A)
(5) c=t2; (C, B, A, :x, :A)
(0) t1=a; (C, B, A, :C, :A)
(1) a=b; (B, B, A, :C, :A)
(2) b=t1; (B, C, A, :C, :A)
(A, B, C, :x, :z) -> (B, C, A, :C, :A)