主要内容

编写线程安全s函数的指南

金宝app®允许您使用多线程编程并行运行s函数,这使得模拟运行速度比串行运行更快。使用s函数的多线程编程要求您使s函数线程安全。创建线程安全的代码需要确保多个线程之间共享的数据受到保护,以便数据和结果符合预期。使用非线程安全的s函数进行模拟可能会导致意外行为。

背景

C/ c++函数用C或c++实现,并内置于称为MEX文件的共享库中。当一个s函数块引用一个共享库时,MATLAB®将s函数块加载到进程中。当多个s函数块引用同一个共享库时,它们也引用初始共享库副本。这个过程导致多个s函数块共享共享库拥有的相同数据。因此,多线程s函数块可以同时访问相同的数据。

此外,如果这些s函数引用相同的资源,多线程s函数块可以同时访问相同的资源(如文件),即使s函数块与不同的s函数相关联。

的指导方针

当s函数可以安全地使用多个线程并发执行时,它通常被认为是线程安全的。将s函数指定为线程安全的,使用ssSetRuntimeThreadSafetyCompliance函数。如果您不确定s函数的线程安全性,请使用这些指导原则来调查并使其线程安全。

数据共享

定义 问题 解决方案

S-function引用使用指针的数据(例如,ssSetUserDatassGetUserDatassSetPWorkValuessGetPWorkValue).s函数可以使用这些指针轻松地共享数据。

多个线程可以使用指针访问相同的数据。如果线程试图同时写入相同的内存位置,它们就违反了线程安全。只要在读取之前、期间或之后没有写操作,来自多个线程的并发读取是安全的,否则会导致不一致的缓存。

在访问由多个线程共享的数据时要小心。

  • 如果数据为只读,则将其设置为常量。

  • 为了控制对数据的访问,可以考虑使用互斥锁。互斥锁也可以确保缓存一致性。

全局变量

定义 问题 解决方案

全局变量是在整个应用程序中可访问的共享数据。

多线程写入不受保护的共享数据是不安全的。只要在读取之前、期间或之后没有写操作,读取就是安全的,否则会导致不连贯的缓存。

  • 如果要写入全局数据,请对其进行本地化,使用互斥锁控制对其的访问,或者更改算法的语义。为了确保缓存的一致性,还可以考虑使用内存围栏,比如原子或互斥锁。

  • 如果您正在阅读,请将您的全局变量设置为常量。

局部静态变量初始化

定义 问题 解决方案

局部静态变量存储在一个位置。

  • 在c++中,局部静态变量在进入函数作用域时初始化,并且不是线程安全的。

  • 在C语言中,只要赋值右边的值(赋给变量的值)是编译时常量,就会在应用程序开始时初始化局部静态变量。这些变量是线程安全的。

如果多个线程同时进入函数作用域,则软件会多次尝试向同一位置写入。即使局部静态变量是常量,这个问题仍然存在。

  • 如果您使用的是c++ 11之前的版本,请使用互斥锁或线程安全的初始化机制来保护局部静态变量的初始化,例如std:: call_onceboost:: call_once

  • 如果使用的是c++ 11或更高版本,则可以保证局部静态变量初始化是线程安全的。

资源

定义 问题 解决方案

资源是向系统显式请求并返回给系统的实体。资源的一些例子包括动态分配的内存、文件、数据库连接和网络套接字。您的应用程序可能需要管理资源。

从多个线程访问资源可能不是线程安全的,例如从多个线程读取和写入文件。即使这些操作是线程安全的,它们也可能不会产生预期的结果。

管理资源时要谨慎。资源的线程安全性取决于它的实现。有关线程安全规范的更多信息,请参阅资源文档。您还可以选择使用互斥锁等机制来保护对资源的访问。

可重入性

定义 问题 解决方案

如果从同一个线程(递归地)多次调用一个函数是安全的,那么这个函数就是可重入的。例如,strtok函数不可重入,因为它保留了要标记的字符串的一些内部状态。如果一个函数不调用不可重入的函数,那么它就是可重入的。

从多个线程调用不可重入函数可能不安全。

使函数可重入。例如:

  • 消除函数保持的状态。

  • 将不可重入的函数调用替换为可重入的等价函数。例如,替换strtokstrtok_r

mexCallMATLAB

定义 问题 解决方案

s函数可以使用mexCallMATLAB函数。

金宝appSimulink代码处理mexCallMATLAB功能不是线程安全的。

不要打电话mexCallMATLAB在s函数中。

无异常代码

定义 问题 解决方案

只要它的子例程在被调用时没有长跳的可能,s函数就没有异常。有关无异常s函数的更多信息,请参见无异常代码

当s函数不是无异常时,它的子例程将通过mexCallMATLAB,它不是线程安全的(参见mexCallMATLAB).

检查跳远时的s函数。如果没有异常,则使用SS_OPTION_EXCEPTION_FREE_CODE ssSetOptions(年代)函数。如果s函数不顾此标志而跳远,则会发生不可预测的行为。

如果s -函数抛出异常,但使用try/catch块捕获异常,则该s -函数是安全的。

数据竞赛

定义 问题 解决方案

当应用程序的输出依赖于执行顺序,从而应用程序的行为在执行之间发生变化时,就会发生数据争用。

应用程序可能有意外的行为。

考虑以下其中一个:

  • 修改你的算法以消除数据竞争。

  • 使用锁来控制代码中关键部分的执行顺序,或者使关键操作原子化。

挥发性

定义 问题 解决方案

挥发性关键字告诉编译器不要优化变量,因为它的值可能会被一些编译器不知道的机制改变。

应用程序可能会错误地使用挥发性实现线程安全。挥发性不提供线程间的原子性或同步。

不要使用挥发性线程安全关键字。

错误状态

定义 问题 解决方案

S-functions使用ssSetErrorStatusssGetErrorStatusssSetLocalErrorStatus,ssGetLocalErrorStatus访问错误状态。

ssSetErrorStatusssGetErrorStatus不是线程安全的。这些函数可以覆盖现有的错误并导致报告不准确的错误。例如,块A可能报告由块B抛出的错误。

使用线程安全的ssSetLocalErrorStatus,ssGetLocalErrorStatus功能。不要使用ssSetErrorStatusssGetErrorStatus

另请参阅

|

相关的话题