1

C スレッドの作成中にポインターを最新のものに渡し、TCL-C スレッド共有変数を持たせるために、Tcl 変数を C 変数にリンクしようとしています (ネイティブの TCL スレッド共有変数関数を使用できるとは思いません)。 . 両方の変数をリンクするのにいくつかの困難があります。これが私のやり方です:

#Tcl code, calling the C function:
set linkedVar 98
puts "linkedVar: $linkedVar"
load [file join [pwd] libCextension[info sharedlibextension]]
set threadId [createThreadC]
puts "Created thread n° $threadId"
puts "linkedVar: $linkedVar"

このcreateThreadC関数は C スレッドを作成し、その ID を返し、 とのリンクを作成しようとしますlinkedVar

// C function called by Tcl
static int
createThreadC_Cmd(
    ClientData cdata,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    int linkedVar=2;
    Tcl_LinkVar(interp, "linkedVar", (char *) &linkedVar, TCL_LINK_INT);
    linkedVar=1;
    ...
    # Thread creation, return Tcl object with thread ID
    ...
    return TCL_OK;  
}

出力は次のとおりです。

linkedVar: 98
Created thread n° -1227199680
linkedVar: 35

CプログラムがしなければならなかったlinkedVarように値が変更されましたが、間違った変数が格納されています.35ではなく1である必要があります(char *) &linkedVar.間違っているのはキャストですか?

4

2 に答える 2

3

あなたはTcl_LinkVar ほぼ正しく使用しています。元のコードは型が正しいです。しかし、それは悪いことではありません!

問題は、Tcl インタープリター (かなり長い寿命を持つ) と短い寿命を持つ C スタック上の変数の間をリンクしていることです。の後createThreadC_Cmd、リンクは未使用のスタックを指し、その直後に別の用途に使用されることがよくあります。これは正式には定義されていない動作であり、非常に悪いことです。あなたがしなければならないことは、C 変数の寿命がインタプリタの寿命と少なくとも同じであることを確認することです。

最も簡単な修正は、グローバル (またはstaticローカル) 変数を使用することです。唯一の欠点は、同じ変数がすべての呼び出しで共有されることcreateThreadC_Cmdです。まったく問題にならないこともありますが、あなたの場合はそうではないと思います。そのため、代わりに別の場所にスペースを割り当てる必要があります。これを行う最も安価な方法は、作成したインタープリターがなくなることを期待していない場合は、 を使用mallocして少しスペースを確保し、そこにリンクを向けることです。その後、メモリをリークして、それについて心配するのをやめることができます (これは汚れていますが、非常に簡単に行うことができます)。クリーンアップしたい場合は、事実上同じことを行いますがfree、メモリである適切なシャットダウン フックを登録します。Tcl には、実際に何がなくなるかに応じて、3 種類のシャットダウン フックがあります。

  1. インタプリタのシャットダウン フックは、Tcl_CallWhenDeleted
  2. スレッドシャットダウン フックは次のように作成されます。Tcl_CreateThreadExitHandler
  3. プロセス/ライブラリシャットダウン フックは で作成されTcl_CreateExitHandlerます (完全にクリーンにする必要がない限り、メモリを削除するためにこれは必要ありません。警告、これらが呼び出された時点でメモリを削除するのは非常に困難です)。

どちらがあなたに適しているかよくわかりません。変数をどれだけ広く共有しているかによって異なります。(スレッド間で共有する予定がないことを願っています。設計上、うまく機能しません。)

于 2012-09-07T10:50:47.747 に答える
1

Donal と同じことを言うつもりでしたが、デモも書きました。ここにあります-基本的に、リンクされた変数の有効期間はインタープリターの有効期間と一致する必要があります。

#include <tcl.h>

typedef struct Shared {
    Tcl_Interp *interp;
    int id;
} Shared;

static int
UpdateCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
    Shared *sharedPtr = (Shared *)clientData;
    if (objc != 1) {
        Tcl_WrongNumArgs(interp, 1, objv, "");
        return TCL_ERROR;
    }
    ++sharedPtr->id;
    return TCL_OK;
}
static void
DeleteProc(ClientData clientData)
{
    Shared *sharedPtr = (Shared *)clientData;
    Tcl_UnlinkVar(sharedPtr->interp, "shared_id");
    Tcl_Release(sharedPtr->interp);
    Tcl_Free(clientData);
}

int DLLEXPORT
Testlink_Init(Tcl_Interp *interp)
{
    Shared *sharedPtr;
    if (Tcl_InitStubs(interp, "8.4", 0) == NULL) {
        return TCL_ERROR;
    }
    sharedPtr = (Shared *)Tcl_Alloc(sizeof(Shared));
    sharedPtr->interp = interp;
    sharedPtr->id = 0;
    Tcl_Preserve(sharedPtr->interp);
    Tcl_LinkVar(interp, "shared_id", (char *)&sharedPtr->id, TCL_LINK_INT);
    Tcl_CreateObjCommand(interp, "update_shared", UpdateCmd, sharedPtr, DeleteProc);
    Tcl_PkgProvide(interp, "testlink", "1.0");
    return TCL_OK;
}

使用法 (msvc 6 を使用したビルドも):

C:\src>cl -nologo -Od -MD -I\opt\tcl\include -DUSE_TCL_STUBS -c tcl_link.c
tcl_link.c

C:\src>link -dll -debug -out:tcl_link.dll tcl_link.obj \opt\tcl\lib\tclstub85.lib
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

C:\src>tclsh
% load tcl_link.dll testlink
% set shared_id
0
% update_shared
% set shared_id
1

これは、コマンドのクリーンアップ機能を利用して物事をクリーンアップする 1 つの方法を示しています。

複数のスレッドを使用し、Tcl インタープリターも使用する場合は、十分に注意する必要があります。Tcl interp は、それが作成されたスレッドに関連付けられています。したがって、Shared 構造体を C スレッド間で受け渡したい場合は、interp メンバーを解放する必要があります。その場合、この構造体の有効期間はアプリケーションによって処理されます。Shared 構造の有効期間が、これが clientData であるコマンドを持つどのインタープリターよりも長い場合、すべて問題なく、インタープリター内でクリーンアップする必要はありません。

于 2012-09-07T10:55:23.120 に答える