我有一个函数,它通过函数指针数组并按顺序调用每个函数。
如果在此过程中更改了函数指针数组的内容,会发生什么?函数调用是否可以被认为足够原子以确保没有意外发生,或者必须注意不要更改函数指针,例如堆栈上的推送正在发生?
下面的一些示例(伪)代码。 init()在启动时运行一次,并且callFunctions()定期运行,比如每秒一次。然后出现changeFunctions()并更改functionPtrArray []的内容。这可能发生在任何时间点,因为代码在OS环境中的不同进程中运行。
void (*functionPtrArray[3]) ( void );
void init( void )
{
functionPtrArray[0] = function1;
functionPtrArray[1] = function2;
functionPtrArray[2] = function3;
}
void callFunctions( void )
{
for ( i = 0; i < 3; i++ )
{
*functionPtrArray[i]();
}
}
void changeFunctions( void )
{
functionPtrArray[0] = newFunction1;
functionPtrArray[1] = newFunction2;
functionPtrArray[2] = newFunction3;
}
答案 0 :(得分:2)
callFunctions()定期运行,比如说每秒一次。然后出现changeFunctions()并更改functionPtrArray []的内容。这可能发生在任何时间点,因为代码在不同的进程中运行,如OS环境。
从您的描述中可以明显看出,functionPtrArray
数组被修改并以不同步的方式由多个线程/进程访问4 data race。
因此,您需要以某种方式提供同步。
答案 1 :(得分:2)
与多线程场景中的任何变量相同。不,除非您使用C11中的_Atomic
限定符,否则它们不能被视为原子。
在许多情况下,函数指针的读取不是原子的。具有16位地址的8位CPU就是一个例子。其中一些架构具有确保16位索引寄存器的安全,不可中断处理的指令,而其他架构则没有。另一个例子是支持超出默认地址总线宽度的扩展内存的任何架构(银行业务,&#34;远指针&#34;)。
如代码所示,函数指针数组不得被另一个线程/进程/ ISR /回调更改,否则可能会发生竞争条件错误。您必须使用信号量/互斥/临界区保护对阵列的访问。
由于函数调用可能会占用一些执行时间,因此您不希望在执行期间阻止所有其他线程。最好在本地复制函数指针,如下面的伪代码:
void callFunctions( void )
{
for ( i = 0; i < 3; i++ )
{
void(*local)(void);
grab_mutex();
local = functionPtrArray[i];
release_mutex();
local(); // call is completely thread-safe
}
}
答案 2 :(得分:2)
编辑:我太长了,被伦丁先发制人抢先一步:https://stackoverflow.com/a/42201493/5592711
重要的一点是:调用C
中的函数可归结为跳转,因此当程序执行函数时,更改用于进行函数调用的假设指针不会改变程序流程中的任何内容,因为指令指针已经在函数内。
例如,在此代码段some_function()
中执行正常,不受ptr
修改的影响。
void(*ptr)(void) = &some_function;
(*ptr)();
// ... in another thread, while 'some_function()' is executing
ptr = &other_function;
但是,你在这里提出一个重要的观点,因为内存操作不是原子的,一些线程可以修改functionPtrArray[0]
元素,而另一个线程正在读取它,这将导致它跳转到一些垃圾地址并将导致您的程序失败,就像在这个假设的例子中一样。
为了确保线程之间的同步,您可以使用mutexes
,例如使用pthread
库(您可以自己使用Google,您会找到大量信息)。
在您的示例中,使用此类同步可能如下所示:
// You can use static mutex initialization
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void callFunctions( void )
{
pthread_mutex_lock(&mutex);
for ( i = 0; i < 3; i++ )
{
*functionPtrArray[i]();
}
pthread_mutex_unlock(&mutex);
}
void changeFunctions( void )
{
pthread_mutex_lock(&mutex);
functionPtrArray[0] = newFunction1;
functionPtrArray[1] = newFunction2;
functionPtrArray[2] = newFunction3;
pthread_mutex_unlock(&mutex);
}
这样,修改函数指针数组并执行函数是互斥的任务。 您应该知道此示例有几个限制:
changeFunctions
,否则您将陷入僵局。