0

我正在尝试将现有的C DLL(明显未管理),实现模糊匹配,作为用户定义函数(UDF)集成到SQL Server中。 UDF是通过一个CLR VB项目实现的。我已经使用这个C代码将近20年来在文本文件上进行字符串匹配而没有困难。它已经被编译在关于太阳下的每个平台上,并且从未崩溃或者给出错误的结果。永远。到现在。从VB中调用非托管C DLL在SQL Server中的UDF UDF

此UDF在SQL SELECT语句的使用看起来是这样的:

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

xudf_fuzzy(参数1,参数2)= 1是神奇在哪里发生。参数1是我们尝试匹配的线索词,而参数2是要测试的表中的字段。如果匹配在一定数量的错误内成功,则UDF返回1,否则返回0.迄今为止这么好。

这里是CLR代码定义模糊UDF并调用C DLL:

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来调用模糊匹配,我每次都会得到正确的结果时间。

如果我使用反对使用,而只使用一个线程上面显示的SQL语句测试数据库上面显示的CLR UDF应用(单核VM)我回到正确的结果每一次为好。

当这个DLL在多核心机器上以CLR UDF模式使用时,一些结果是错误的。结果总是有点偏离,但并不一致。

292条记录应该在前两个测试用例中匹配并执行。

在CLR UDF多线程情况下的结果会回来用273,284,298,290等

所有的C DLL存储的是字符数组。没有使用内存分配。我的理解是,如果SQL Server在多线程模式下使用这个CLR应用程序,那么这些线程都被分配了各自的数据空间。

我需要以某种方式“销”的字符串之前,我把它们发送到C DLL?我无法弄清楚如何继续。

回答

1

钢钉是不是你的问题,你的C代码不是线程安全的。 C代码使用全局变量STRCT。该全局变量只有一个实例将在所有线程中共享。每个线程将使用不同的值更新STRCT变量,这会导致错误的结果。

您将不得不重构C代码,以便它不依赖于全局变量的共享状态。

您可以把它用__declspec(thread)将使用Thread Local Storage所以每个线程都有自己的变量副本宣布STRCT工作。但是,这对于每个非托管线程都是唯一的,并且不保证有one-to-one mapping between managed and un-managed threads

更好的解决方案是完全摆脱共享状态。您可以通过在setClue中分配SRCH_STRUCT并返回该指针来完成此操作。然后,每次调用srchString时都会将该指针作为参数使用SRCH_STRUCT指针。 VB.Net代码只需要像在IntPtr中一样处理这个结构,它不需要知道任何关于SRCH_STRUCT的定义。请注意,您还需要向DLL添加一个新函数以释放分配的SRCH_STRUCT

+0

shf301我希望我知道如何给你评分或将问题标记为已回答。你给的答案解决了眼前的问题。谢谢。我还决定尝试将模糊代码转换为C#,以避免从托管VB跳转到C DLL导致的压倒性性能下降。无论如何,感谢帮助队友! – codeThug