使现有的ANSI C代码线程安全且可重入

时间:2013-11-03 11:10:41

标签: c multithreading posix reentrancy

我正在研究一个旧的传统ANSI C系统,它充斥着许多全局变量。我是重组现有代码库的团队的一员,使代码重入并且线程安全。我在编写线程安全和重入ANSI C代码here时找到了有用的资料。

根据我(理所当然不完美)的理解,我提出了一个关于如何继续的建议 - 但我已经提出了一些需要解决的问题,并决定最好来这里查找在实际开始编码之前设计事物的最佳方式。

/******************************************************************/
/*                                                                */
/* CURRENT situaton:                                              */
/* Global variables scattered accross a multitude of source files */
/*                                                                */
/******************************************************************/
static struct myStruct some_struct;
static long instance_counter;
static char[MAX_STR_LEN] instance_name;
static dbConnection * db_conn;
static SharedMemoryPtr * shrmem_ptr;




/*******************************************************************/
/*                                                                 */
/* PROPOSED solution:                                              */
/* Wrap all global variables into one thread local struct and      */
/* provide getter/setter* funcs for the variables (of course, this */
/* means that I will have to modify existing code to use the       */
/* context variable and the getter/setter methods instead of       */
/* directly accessing the global variables)                        */
/*                                                                 */ 
/*******************************************************************/

/* Thread local variables stored in ONE source file */

struct myStruct some_struct;
long instance_counter;
char[MAX_STR_LEN] instance_name;
dbConnection * db_conn;
SharedMemoryPtr * shrmem_ptr;


/* Thread local variable that provides getter/setter funcs to 'globals' */

typedef struct _ctx
{
    /* Getter functions */
    variable1_type (getter_func1*)(void);
    variable2_type (getter_func2*)(void);

    /* Setter functions */
    void (setter_func1*)(variable1_type v1);
    void (setter_func2*)(variable2_type v2);
} Context;

我有三个主要问题:

  1. 我采取的方法是好的(即最好的还是更好的)?如果没有,是否有更好的推荐(即“最佳实践”)方式做我想做的事情?

  2. 如果在一个线程中更改了线程局部变量,那么更改是否反映在其他线程中?如果答案为否(我怀疑它是),那么这意味着可以使用不同的变量值运行两个线程。在我能想到的几乎所有应用程序中,这都是不可接受的 - 那么现有的多线程应用程序如何避免这种情况呢?我的基本理解告诉我在写入之前必须获取锁,并且当有任何线程读取时,写入器线程必须阻塞。如果情况并非如此,那么我希望澄清正确的事件顺序。我还要感谢一些伪代码,它将展示如何使用我的示例数据结构实现这个读/写操作。

  3. 我在上下文结构中编写的getter函数伪代码理想情况下应该返回指针,原因很明显(为了避免每次检索到一个可能的巨大数据结构)。但是,根据我在前面的问题(提供的链接)中提到的IBM页面:

  4.   

    可重入函数不会在连续调用中保存静态数据,   它也不会返回指向静态数据的指针。所有数据均由提供   函数的调用者。

    所以(据我所知),getter函数不能返回指向静态数据的指针(除非我弄错了)。有人会澄清。另外,如果我不应该从gettter函数返回指针,是否有任何方法/技术可用于防止/避免返回数据副本(正如我所说,一些结构非常巨大/重要)。

    [[除了]]

    我正在开发Ubuntu 12.04 LTS,所以我对POSIX兼容解决方案很感兴趣。

1 个答案:

答案 0 :(得分:0)

你应该问每个变量的第一个问题是:是吗?

  • 只读(初始化一次)
  • 同步读/写(所有线程始终需要一致的副本)
  • 非同步读/写(每个线程需要该变量,但不需要一致的副本或对私有副本感到满意)

分析器或调试器将能够告诉您您的类型。

如果所有变量属于第三种类型,你的方法可以正常工作,你自己已经提到过这可能不是真的。

对于第一种类型(只读),只要在并行代码之前进行初始化,就可以保持原样。

对于第三种类型,只需将__thread修饰符置于其前面并确保它在并行部分中初始化。

第二种类型是最困难的类型。您将需要使用某种同步来访问它:基本上只允许一个线程一次访问它。

这有三个主要模型:

  • 单锁。每次访问都必须首先获取锁,使用变量,释放锁。查看pthread_mutex_*函数。此外,您还必须确定单个锁保护多少个变量,它可以像一个大结构或数组一样精细。
  • 读取器/写入器锁定 - 如果允许多个并发读取访问,但是一次只能进行一次写入访问,则这是更好的选择。有一个pthread_mutex_*变体可以做到这一点。
  • 最后原子 - 这些是特殊的硬件指令,能够一次修改同步的机器字。 gcc将为您提供atomic builtins

希望这有点帮助。