C 在更改数组内容时通过位于数组中的指针调用函数
我有一个函数,它通过一个函数指针数组并按顺序调用每个函数 如果函数指针数组的内容在此过程中发生更改,会发生什么情况?函数调用是否可以被认为是原子的,以确保不会发生意外情况,或者必须注意不要在堆栈上进行推送时更改函数指针 下面是一些示例(伪代码)。init()在启动时运行一次,callFunctions()定期运行,比如说每秒运行一次。然后出现changeFunctions(),并更改functionPtrArray[]的内容。这可能在任何时候发生,因为代码在类似操作系统的环境中的不同进程中运行C 在更改数组内容时通过位于数组中的指针调用函数,c,function,function-pointers,C,Function,Function Pointers,我有一个函数,它通过一个函数指针数组并按顺序调用每个函数 如果函数指针数组的内容在此过程中发生更改,会发生什么情况?函数调用是否可以被认为是原子的,以确保不会发生意外情况,或者必须注意不要在堆栈上进行推送时更改函数指针 下面是一些示例(伪代码)。init()在启动时运行一次,callFunctions()定期运行,比如说每秒运行一次。然后出现changeFunctions(),并更改functionPtrArray[]的内容。这可能在任何时候发生,因为代码在类似操作系统的环境中的不同进程中运行
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;
}
void(*functionPtrArray[3])(void);
void init(void)
{
functionPtrArray[0]=function1;
functionPtrArray[1]=function2;
functionPtrArray[2]=function3;
}
void调用函数(void)
{
对于(i=0;i<3;i++)
{
*函数trarray[i]();
}
}
void变更函数(void)
{
functionPtrArray[0]=newFunction1;
functionPtrArray[1]=newFunction2;
functionPtrArray[2]=newFunction3;
}
callFunctions()会定期运行,比如说每秒运行一次。然后出现changeFunctions(),并更改functionPtrArray[]的内容。这可能在任何时候发生,因为代码在类似操作系统的环境中的不同进程中运行
从您的描述中可以明显看出,functionPtrArray
array是由多个线程/进程以非同步方式修改和访问的,这是一种错误。
因此,您需要以某种方式提供同步
callFunctions()会定期运行,比如说每秒运行一次。然后出现changeFunctions(),并更改functionPtrArray[]的内容。这可能在任何时候发生,因为代码在类似操作系统的环境中的不同进程中运行
从您的描述中可以明显看出,functionPtrArray
array是由多个线程/进程以非同步方式修改和访问的,这是一种错误。
因此,您需要以某种方式提供同步。这与多线程场景中的任何变量的情况相同。不,除非您使用C11中的
\u原子
限定符,否则不能将其视为原子
在许多情况下,函数指针的读取不是原子的。具有16位地址的8位CPU就是一个例子。其中一些体系结构有一条指令来确保16位索引寄存器的安全、不可中断处理,而其他体系结构则没有。另一个例子是任何支持超出默认地址总线宽度的扩展内存的体系结构(banking,“远指针”)
正如代码所示,函数指针数组不能被另一个线程/进程/ISR/回调更改,否则可能会发生争用条件错误。您必须使用信号量/mutex/critical部分保护对数组的访问
由于函数调用可能会占用一些执行时间,因此您不希望在执行期间阻塞所有其他线程。最好在本地复制函数指针,如下伪代码所示:
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
}
}
void调用函数(void)
{
对于(i=0;i<3;i++)
{
无效(*本地)(无效);
抓取互斥对象();
本地=函数ptrarray[i];
释放互斥锁();
local();//调用是完全线程安全的
}
}
这与多线程场景中的任何变量的情况相同。不,除非您使用C11中的\u原子
限定符,否则不能将其视为原子
在许多情况下,函数指针的读取不是原子的。具有16位地址的8位CPU就是一个例子。其中一些体系结构有一条指令来确保16位索引寄存器的安全、不可中断处理,而其他体系结构则没有。另一个例子是任何支持超出默认地址总线宽度的扩展内存的体系结构(banking,“远指针”)
正如代码所示,函数指针数组不能被另一个线程/进程/ISR/回调更改,否则可能会发生争用条件错误。您必须使用信号量/mutex/critical部分保护对数组的访问
由于函数调用可能会占用一些执行时间,因此您不希望在执行期间阻塞所有其他线程。最好在本地复制函数指针,如下伪代码所示:
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
}
}
void调用函数(void)
{
对于(i=0;i<3;i++)
{
无效(*本地)(无效);
抓取互斥对象();
本地=函数ptrarray[i];
释放互斥锁();
local();//调用是完全线程安全的
}
}
编辑:我太长了,被伦丁抢先了:
重要的一点是:在C
中调用函数归结为一个跳转,因此当程序执行函数时,更改用于调用函数的假设指针不会改变程序流的任何内容,因为指令指针已经在函数中
例如,在此代码段中,某些函数()
将正常执行,并且不受ptr
修改的影响
void(*ptr)(void) = &some_function;
(*ptr)();
// ... in another thread, while 'some_function()' is executing
ptr = &other_function;
然而,您在这里提出了一个重要的观点,因为内存操作不是原子的,一些线程可以在另一个线程读取functionPtrArray[0]
元素时修改它,这将导致它跳转到某个垃圾地址,并导致您的程序失败,如本假设示例所示
为了确保线程之间的同步,您可以使用mutex
,例如使用pthread
库(您可以自己用谷歌搜索,您会发现大量信息)
在您的示例中,使用这种同步可能类似于:
// 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);
}
//您可以使用静态互斥体初始化
pthread\u mutex\u t mutex=pthread\u mutex\u INIT