简体   繁体   English

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

[英]Calling a function by pointer located in an array while changing the array content

I have a function that goes through an array of function pointers and calls each of these functions in order. 我有一个函数,该函数遍历函数指针数组,并按顺序调用这些函数。

What happens if the contents of the array of functions pointers is changed during this process? 如果在此过程中更改了函数指针数组的内容,会发生什么情况? Can the function calls be considered atomic enough to be sure that nothing unexpected happens, or must care be taken not to change the function pointers while for example the push on the stack is taking place? 是否可以将函数调用视为足够原子的函数,以确保不会发生任何意外情况,还是必须注意在进行堆栈推送时不要更改函数指针?

Some example (pseudoish) code below. 下面是一些示例(伪)代码。 init() is run once at startup, and callFunctions() is run periodically, say once each second. init()在启动时运行一次,而callFunctions()则定期运行,例如每秒运行一次。 Then changeFunctions() comes along and changes the content of functionPtrArray[]. 然后changeFunctions()出现并更改functionPtrArray []的内容。 This may happen at any point of time, since the code is run in different processes a OS like environment. 这可能会在任何时间发生,因为代码是在类似环境的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;
}

callFunctions() is run periodically, say once each second. callFunctions()定期运行,例如每秒运行一次。 Then changeFunctions() comes along and changes the content of functionPtrArray[]. 然后changeFunctions()出现并更改functionPtrArray []的内容。 This may happen at any point of time, since the code is run in different processes a OS like environment. 这可能会在任何时间发生,因为代码是在类似环境的OS的不同进程中运行的。

From your description, it's obvious that functionPtrArray array is modified and accessed 4by more than one thread/process(es) in an unsychronized way which is a data race . 从您的描述中,很明显,不止一个线程/进程以一种不同步的方式修改了functionPtrArray数组,并通过多个线程/进程对其进行了访问,这是一种数据竞争 So, you'd need to provide synchronization one way or other. 因此,您需要以一种或其他方式提供同步。

It's the same case as for any variable in a multi-threaded scenario. 与多线程方案中的任何变量一样。 No, they cannot be considered atomic unless you use the _Atomic qualifier from C11. 不可以,除非您使用C11中的_Atomic限定符,否则它们不能被视为原子的。

There are many situations where a read of a function pointer will not be atomic. 在许多情况下,对函数指针的读取不是原子的。 8 bit CPUs with 16 bit addresses is one example. 具有16位地址的8位CPU就是一个示例。 Some of those architectures have an instruction to ensure safe, non-interruptable processing of a 16 bit index register, others do not. 这些体系结构中的某些具有一条指令,以确保对16位索引寄存器进行安全,不间断的处理,而其他一些则没有。 Another example is any architecture that supports extended memory beyond the default address bus width (banking, "far pointers"). 另一个示例是支持超出默认地址总线宽度(存储区,“远指针”)的扩展内存的任何体系结构。

As the code stands, the array of function pointers must not get changed by another thread/process/ISR/callback, or race condition bugs may happen. 如代码所示,函数指针的数组一定不要被另一个线程/进程/ ISR /回调所更改,否则可能会发生竞争条件错误。 You have to protect access to the array with semaphore/mutex/critical section. 您必须使用信号量/互斥量/关键部分来保护对数组的访问。

Since the function calls may take up some execution time, you don't want to block all other threads during that execution though. 由于函数调用可能会占用一些执行时间,因此您不想在执行期间阻塞所有其他线程。 It is probably best to copy down the function pointers locally, as in this pseudo-code: 最好是在本地复制函数指针,如以下伪代码所示:

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
  }
}

edit : I was too long and pre-empted by Lundin : https://stackoverflow.com/a/42201493/5592711 编辑 :我太长了,被隆丁抢先了: https : //stackoverflow.com/a/42201493/5592711

An important point : calling a function in C boils down to a jump, so when the program is executing the function, changing a hypothetical pointer used to make the function call will not change anything to the program flow, since the instruction pointer is already within the function. 重要的一点:在C调用函数归结为跳转,因此,在程序执行函数时,更改用于进行函数调用的假设指针不会改变程序流,因为指令指针已经在内部功能。

For example, in this snippet some_function() will execute fine and is not affected by the modification of ptr . 例如,在此代码段中, some_function()将执行良好,并且不受ptr修改的影响。

void(*ptr)(void) = &some_function;
(*ptr)();
// ... in another thread, while 'some_function()' is executing
ptr = &other_function;

However, you're raising an important point here, because memory operations are not atomic, some thread can modify the functionPtrArray[0] element while another thread was reading it, that will lead it to jump to some garbage address and will cause your program to fail, like in this hypothetical example. 但是,您在这里提出了一个重要的观点,因为内存操作不是原子的,所以某些线程可以在另一个线程正在读取它的同时修改functionPtrArray[0]元素,这将导致它跳转到某些垃圾地址并导致您的程序失败,就像在这个假设的例子中一样。

To ensure synchronization between threads, you can use mutexes , for example using the pthread library (you can Google that yourself, you will find plenty of information). 为了确保线程之间的同步,您可以使用mutexes ,例如使用pthread库(可以使用Google自己,您会找到很多信息)。

In your example, using such synchronization could look like : 在您的示例中,使用这种同步可能类似于:

// 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);
}

This way, modifying your function pointer array and executing the functions are mutually-exclusive tasks. 这样,修改函数指针数组和执行函数是互斥的任务。 You should be aware that this example has several limitations : 您应该意识到此示例有几个局限性:

  1. The mutex is locked during the whole functions execution (that is not needed, you can just lock it to read the function pointers, unlock and then execute them) 互斥锁在整个函数执行期间被锁定(不需要,您可以将其锁定以读取函数指针,解锁然后执行它们)
  2. The functions must not call changeFunctions themselves, otherwise you will end up in a deadlock. 这些函数不得自行调用changeFunctions ,否则将导致死锁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM