1. 副作用
函数副作用:指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。
表达式副作用:在表达式求值过程中,需要获取变量的值,但并不改变这些变量的值,这样的表达式称为无副作用的表达式。一个表达式在求值过程中,对使用的变量不但引用,对它们的值还加以改变,这样的表达式称为有副作用的表达式。例如 count++,表达式获取的是 count 值,但在获取过程中对 count 变量的值进行了加 1 操作,故此表达式是有副作用的表达式。
纯函数:输入输出数据流全是显式的。 显式的意思是,函数与外界交换数据只有一个唯一渠道——参数和返回值。函数从函数外部接受的所有输入信息都通过参数传递到该函数内部。函数输出到函数外部的所有信息都通过返回值传递到该函数外部。
此外,还有非纯函数,引用透明,详细可见链接:https://www.cnblogs.com/snandy/archive/2011/08/14/2137898.html。
2. 缩小副作用范围,解决线程安全问题
从表达式副作用角度考虑 12-3 小节的 count++ 问题,若 count++ 副作用修改的 count 变量不是其他线程的变量,则不存在线程安全问题。故将 count 定义为局部变量,而并非全局变量。代码如下所示:
#include <stdio.h>
#include <tinycthread.h>
int Counter(void *arg){
int local_count = 0;
for (int i = 0; i < 1000000; ++i) {
local_count++;
}
return local_count;
}
void TestCounterByThread(){
thrd_t t_1;
thrd_t t_2;
// create two threads to run the Counter function
thrd_create(&t_1, Counter, NULL);
thrd_create(&t_2, Counter, NULL);
int count = 0;
int result;
// free up resources
thrd_join(t_1, &result);
count += result;
thrd_join(t_2, &result);
count += result;
printf("count: %d", count); // count: 2000000
}
这种方法的解决思路与 _Thead_local,tss_t 类似,即从根本上杜绝线程安全问题的发生。线程之间不再享有共享资源,每个线程不操作其他线程的变量,仅操作线程内部的变量。
以 count++ 问题为例,该问题的需求是启用两个线程,将主线程的 count 变量加 20w 次。可以设置锁等操作,将两个线程并发执行,修改主线程变量最终得到 20w。也可以在每个线程内部加 10w 次,将两个线程的结果进行合并,进而将主线程 count 变量进行 20w 次的相加。核心仍为,不操作主线程的变量,仅操作线程内部的变量。