在更改数组内容时,通过位于数组中的指针调用函数

时间:2017-02-13 10:00:35

标签: c function function-pointers

我有一个函数,它通过函数指针数组并按顺序调用每个函数。

如果在此过程中更改了函数指针数组的内容,会发生什么?函数调用是否可以被认为足够原子以确保没有意外发生,或者必须注意不要更改函数指针,例如堆栈上的推送正在发生?

下面的一些示例(伪)代码。 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;
}

3 个答案:

答案 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);
}

这样,修改函数指针数组并执行函数是互斥的任务。 您应该知道此示例有几个限制:

  1. 在整个函数执行期间锁定互斥锁(不需要,你可以锁定它来读取函数指针,解锁然后执行它们)
  2. 这些功能必须自行调用changeFunctions,否则您将陷入僵局。