1

我有一个 Tcl 主程序,我想从中创建一个 C 线程。然后我需要在两个线程之间共享信息: C 线程的进程经常更新输入/输出。我看到我的问题有两种可能的解决方案:(1) 将 Tcl 的线程共享变量移植到 C,但我在 TCL-C API 中没有看到任何关于它的信息。(2) 创建 Tcl-C 链接变量并将其用作 C 线程创建期间的参数。后一种想法似乎行不通。这是C代码:

#include <tcl.h>

/*
startRoutine
*/
static void startRoutine (ClientData clientData) {
 int *Var;
 Var= (int *) clientData;
 int locA=0;
 int j;
 int k;
 while (1) {    
     if (locA=!*Var) {
         // Modify Tcl-C shared variable
         locA=2 * *Var;
         *Var=locA;
         for (j=0; j<100; j++){}
     } else {
         for (k=0; k<100; k++){}
     }
  }
 }




static int
createThreadC_Cmd(
    ClientData cdata,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{

    // Contains the ID of the newly created thread
    Tcl_ThreadId id;    
    // Thread argument
    ClientData limitData;

    // Transfering global var argument to the created thread
    limitData=cdata;

    // Thread creation
    id=0;
    Tcl_CreateThread(&id, startRoutine, limitData, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS);

    // Wait thread process, before returning to TCL prog
    int i;
    int aa;
    for (i=0 ; i<10000000 ; i++){
        aa=i;
    }

    // Return thread ID to tcl prog to allow mutex use
    Tcl_SetObjResult(interp, Tcl_NewIntObj((int) id));
    return TCL_OK;  
}


int DLLEXPORT

Behavcextension_Init(Tcl_Interp *interp)
{

    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
        return TCL_ERROR;
    }

    // Create global Var
    int *sharedPtr;
    int linkedVar=0;
    sharedPtr=&linkedVar;
    Tcl_LinkVar(interp, "linkedVar", (char *) sharedPtr, TCL_LINK_INT);

    Tcl_CreateObjCommand(interp, 
        "createThreadC", createThreadC_Cmd, sharedPtr, NULL);
    return TCL_OK;

}

这是 Tcl 代码:

# linkedVar initial value in Tcl, will be overwritten by C Tcl_LinkVar() function
set linkedVar 98
puts "linkedVar: $linkedVar"

# Thread creation
#------------------
load [file join [pwd] libBehavCextension[info sharedlibextension]]
set threadId [createThreadC]
puts "Created thread $threadId, waiting"
# When Tcl_LinkVar() is called, initiate linkedVar at 2
puts "linkedVar: $linkedVar"

# Function inside thread should modify linkedVar into linkedVar*2
set linkedVar 98
after 5000
puts "linkedVar: $linkedVar"

终端输出在这里:

Main thread ID: tid0xb779b6c0
linkedVar: 98
Created thread -1227252928, waiting
linkedVar: 2
linkedVar: 98

最后的结果应该是 2*98=196。Tcl 和 C 之间的 LinkVar 创建是好的(我们在创建链接后得到 2),但是将 LinkVar 传递给 Thread 是 KO。欢迎任何关于它为什么不起作用/如何解决它的解决方案或解释!

4

1 回答 1

1

问题与另一个问题相同。您正在为 C 堆栈上 C 端的变量分配存储空间,该函数在不久后终止的函数中。在函数 ( ) 终止后引用该变量(位于linkedVar中)是未定义行为。实际发生的情况是,实际存储用于其他一些函数调用(谁知道做什么),因此包含的值是任意的,更改它可能会导致“令人兴奋”的行为。Behavcextension_InitBehavcextension_Init

您正在寻找一个在完成后存在的变量Behavcextension_Init,因此不得在该函数的堆栈中分配它。最简单的方法是这样的:

int DLLEXPORT
Behavcextension_Init(Tcl_Interp *interp)
{
    int *sharedPtr;

    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
        return TCL_ERROR;
    }

    sharedPtr = (int *) Tcl_Alloc(sizeof(int));  // Allocate
    *sharedPtr = 0;                              // Initialize
    Tcl_LinkVar(interp, "linkedVar", (char *) sharedPtr, TCL_LINK_INT);

    Tcl_CreateObjCommand(interp, 
        "createThreadC", createThreadC_Cmd, sharedPtr, NULL);
    return TCL_OK;
}

注意事项

  1. 这会泄漏内存,因为没有匹配Tcl_FreeTcl_Alloc. 对于每个进程分配一次的内存,这不是什么大问题。毕竟,它只有几个字节,操作系统会在退出时回收它。
  2. 从与写入变量不同的线程读取变量时,这是不安全的;根本无法保证它会起作用。它可能会起作用,因为它只是一个整数,但你依赖于硬件来合作。正确的做法是分配一个包含变量和合适互斥锁的结构,并用互斥锁保护对变量的访问(无论是读取还是写入)。这反过来又要求你不要使用Tcl_LinkVar——它对受互斥保护的内存一无所知——而Tcl_LinkVar只是一个包装轮Tcl_TraceVar,它提供了一个回调,用于在 Tcl 的变量之间进行耦合(参见Tcl_GetVarTcl_SetVar) 和 C 变量;编写自己的知道如何进行互斥保护处理的也不难。(如果您有兴趣,请获取源代码Tcl_LinkVar并自行调整;它不使用任何私有 API 调用。)
于 2012-09-10T12:52:19.507 回答