0

あいまい一致を実装する既存の C DLL (明らかに管理されていない) をユーザー定義関数 (UDF) として SQL Server に統合しようとしています。UDF は、CLR VB プロジェクトで実装されます。私はこの C コードを 20 年近く使用して、テキスト ファイルの文字列の照合を滞りなく行ってきました。太陽の下でほぼすべてのプラットフォームでコンパイルされており、クラッシュしたり、誤った結果を与えたりすることはありません。これまで。今まで。

SQL SELECT ステートメントでのこの UDF の使用法は、次のようになります。

SELECT Field FROM Table WHERE xudf_fuzzy('doppler effect', Field) = 1;

xudf_fuzzy(Param1, Param2) = 1で魔法が起こります。Param1 は照合しようとしている手がかりの単語で、Param2 はテスト対象のテーブルのフィールドです。一致が特定のエラー数内で成功した場合、UDF は 1 を返し、そうでない場合は 0 を返します。

ファジー UDF を定義し、C DLL を呼び出す CLR コードを次に示します。

Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports System.Text
Imports Microsoft.SqlServer.Server
Imports System.Runtime.InteropServices


Partial Public Class fuzzy

<DllImport("C:\Users\Administrator\Desktop\fuzzy64.dll", _
           CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function setClue(ByRef clue As String, ByVal misses As Integer) As       Integer
End Function

<DllImport("C:\Users\Administrator\Desktop\fuzzy64.dll", _
           CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function srchString(ByRef text1 As String) As Integer
End Function


<Microsoft.SqlServer.Server.SqlFunction()> Public Shared Function _
        xudf_fuzzy(ByVal strSearchClue As SqlString, ByVal strStringtoSearch As SqlString) As Long

    Dim intMiss As Integer = 0
    Dim intRet As Integer
    Static Dim sClue As String = ""

    xudf_fuzzy = 0

    ' we only need to set the clue whenever it changes '
    If (sClue <> strSearchClue.ToString) Then
        sClue = strSearchClue.ToString
        intMiss = (Len(sClue) \ 4) + 1
        intRet = setClue(sClue, intMiss)
    End If

    ' return the fuzzy match result (0 or 1) '
    xudf_fuzzy = srchString(strStringtoSearch.ToString)

End Function

呼び出される C コードのフロント エンドを次に示します。STRCT は、すべてのグローバル ストレージが存在する場所です。

fuzzy.h
typedef struct {
short int INVRT, AND, LOWER, COMPL, Misses;
long int num_of_matched;
int D_length;
unsigned long int endposition, D_endpos;
unsigned long int Init1, NOERRM;
unsigned long int Mask[SYMMAX];
unsigned long int Init[MaxError];
unsigned long int Bit[WORDSIZE+1];
unsigned char prevpat[MaxDelimit];
unsigned char _buffer[Max_record+Max_record+256];
unsigned char _myPatt[MAXPAT];
} SRCH_STRUCT;


fuzzy.c
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <wtypes.h>
#include <time.h>
#include "fuzzy.h"

// call exports
__declspec(dllexport) int CALLBACK setClue(char**, int*);
__declspec(dllexport) int CALLBACK srchString(char**);

SRCH_STRUCT STRCT = { 0 };
int cluePrep(unsigned char []);
int srchMin(unsigned char [], int);

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

int CALLBACK setClue(char **pattern, int *misses)
{
    int i;
    unsigned char *p;

    // code to do initialization stuff, set flags etc. etc.    
        STRCT.Misses = (int)misses;
        p = &(STRCT._myPatt[2]);
        STRCT._myPatt[0] = '\n';
        STRCT._myPatt[1] = SEPCHAR;
        strcpy((char *)p, *pattern);
        //blah blah
    // end setup stuff

    i = cluePrep(STRCT._myPatt);

    return 0;
}


int CALLBACK srchString(char **textstr)
{
    int res,i = Max_record;
    unsigned char c;
    char *textPtr = *textstr;

    STRCT.matched = 0;

    //clean out any non alphanumeric characters while we load the field to be tested
    while ((c = *textPtr++)) if ( isalpha(c) || isdigit(c) || c == ' ' ) STRCT._buffer[i++] = c;
    STRCT._buffer[i] = 0;

    // do the search
    res =  srchMin(STRCT.pattern, STRCT.Misses);

    if (res < 0) return res;

    return STRCT.matched;

}

リンクされているランタイム ライブラリは次のとおりです。 マルチスレッド DLL (/MD)

呼び出し規約は次のとおりです。__cdecl (/Gd)

ここが変なところです。テスト データベースからレコードセット全体を取得し、この DLL を呼び出して一度に 1 つずつすべてのレコードを反復処理してあいまい一致を呼び出す通常のドット ネット アプリケーション (コードは示していません) がある場合、毎回正しい結果が返されます。時間。

1 つのスレッド (シングル コア VM)のみを使用しながら、上記の SQL ステートメントを使用してテスト データベースに対して上記の CLR UDF アプリケーションを使用すると、毎回正しい結果が得られます。

この DLL をマルチコア マシンの CLR UDF モードで使用すると、一部の結果が正しくありません。結果は常に少しずれていますが、一貫性はありません。

最初の 2 つのテスト ケースでは、292 レコードが一致し、一致するはずです。

CLR UDF マルチスレッドの場合、結果は 273、284、298、290 などで返されます。

C DLL 内のストレージはすべて文字配列です。メモリ割り当ては使用されていません。また、SQL Server がこの CLR アプリをマルチスレッド モードで使用している場合、スレッドにはすべて独自のデータ スペースが割り当てられることも理解しています。

文字列を C DLL に送信する前に、何らかの方法で文字列を "固定" する必要がありますか? 進め方がわかりません。

4

1 に答える 1

1

ピン留めは問題ではありません。C コードはスレッドセーフではありません。C コードはグローバル変数を使用しますSTRCT。すべてのスレッドで共有されるグローバル変数のインスタンスは 1 つだけです。STRCT各スレッドは異なる値で変数を更新するため、誤った結果が生じる可能性があります。

グローバル変数の共有状態に依存しないように、C コードをリファクタリングする必要があります。

各スレッドが変数の独自のコピーを取得するように、スレッドローカルストレージを使用する __declspec(thread) でSTRCT宣言することで、それを機能させることができる場合があります。ただし、これは各アンマネージド スレッドに固有のものであり、マネージド スレッドとアンマネージド スレッドの間に 1 対 1 のマッピングがあるという保証はありません。

より良い解決策は、共有状態を完全に取り除くことです。SRCH_STRUCTinを割り当ててsetClueそのポインタを返すことでこれを行うことができます。次に、への各呼び出しsrchStringは、そのSRCH_STRUCTポインターをパラメーターとして受け取ります。VB.Net コードは、この構造体を IntPtr のように扱うだけでよく、 の定義について何も知る必要はありませんSRCH_STRUCT。割り当てられた .dll の割り当てを解除するには、新しい関数を DLL に追加する必要があることに注意してくださいSRCH_STRUCT

于 2013-04-02T04:52:33.120 に答える