C# 停止使用DB的线程

C# 停止使用DB的线程,c#,mysql,multithreading,C#,Mysql,Multithreading,我正在编写一个自动完成脚本 当用户停止键入时,他的输入将发送到服务器,服务器将在DB中查找一些匹配项 如果要找人,“奥巴马”应该返回“巴拉克·奥巴马” 我希望此搜索限制为~500毫秒。如果需要更长时间,我想中止搜索,只返回及时找到的结果 我首先寻找一个完美的匹配(这一个不会被打断),然后我寻找一个部分匹配(这一个可以被打断): 评分是一种便于对结果进行排序的结构 private struct Scoring { public string value; public int s

我正在编写一个自动完成脚本

当用户停止键入时,他的输入将发送到服务器,服务器将在DB中查找一些匹配项

如果要找人,“奥巴马”应该返回“巴拉克·奥巴马”

我希望此搜索限制为~500毫秒。如果需要更长时间,我想中止搜索,只返回及时找到的结果

我首先寻找一个完美的匹配(这一个不会被打断),然后我寻找一个部分匹配(这一个可以被打断):

  • 评分是一种便于对结果进行排序的结构

    private struct Scoring
    {
        public string value;
        public int score;
    }
    

  • 目标是快速获得结果(不一定全部)

    问题
    • 我在
      conn.Close()上~30秒后随机获得Timeoutexception
    • 我在第一个
      Cmd.ExecuteReader()上随机获得NullReferenceException调用
      FindOtherBrands
    谁能解释一下原因吗? 我是做错了什么还是有解决办法


    我猜TimeoutException是因为我在命令执行期间试图关闭连接,我可以删除/取消该查询吗?

    我会采取不同的方法。由于您查询的是一个自然异步的数据库,因此可以使用
    async wait
    来查询数据。这样,您就可以传递一个设置为超时的
    CancellationToken
    ,您将在每次读取时监视该超时:

    例如:

    private async Task FindOtherBrands(string term, 
                                       Dictionary<int, Scoring> results, 
                                       CancellationToken cancellationToken)
    {
        MySqlCommand cmd;
        string requete;
        requete = "SELECT m.id, m.nom as label 
                   FROM marque m 
                   WHERE m.nom LIKE '" + term + "%'";
    
        cmd = new MySqlCommand(requete, conn);
        var Rd = await cmd.ExecuteReaderAsync();
        while (Rd != null && await Rd.ReadAsync())
        {
            cancellationToken.ThrowIfCancellationRequested();
    
            int id = int.Parse(Rd["id"].ToString());
            if (!results.ContainsKey(id))
            {
                results.Add(id, new Scoring()
                {
                    score = 100,
                    value = Rd["label"].ToString()
                });
            }
        }
    
        Rd.Close();
        requete = "SELECT m.id, m.nom as label 
                   FROM marque m 
                   WHERE m.nom LIKE '%" + term + "%'";
    
        cmd = new MySqlCommand(requete, conn);
        Rd = await Cmd.ExecuteReaderAsync();
        while (Rd != null && await Rd.ReadAsync())
        {
            cancellationToken.ThrowIfCancellationRequest();
            int id = int.Parse(Rd["id"].ToString());
            if (!results.ContainsKey(id))
            {
                results.Add(id, new Scoring()
                {
                    score = 10,
                    value = Rd["label"].ToString()
                });
            }
        }
        Rd.Close();
    }
    

    旁注-您的查询很容易被SQL注入。不应该使用字符串连接。请改用查询参数。

    您使用的是哪个.NET版本?在中止线程之前关闭数据库资源是明智的。遗憾的是,数据库无法在一段时间内返回所有结果。您可以请求所有结果,但不能保证数据库在找到结果后会立即将其发送给您。唯一可靠的方法是在循环中请求第n个结果并设置命令超时。它的效率非常低。我使用.NET4.5@罗伯特麦基:我知道,这就是为什么我搜索
    name=“{term}”
    然后搜索
    name,比如“{term}%”
    ,最后搜索
    name,比如“%%{term}%”
    你不需要使用单独的线程。只需设置commandTimeout属性。在try/catch块中使用,这样就可以捕获数据库命令超时,但这样很可能会得到所有结果,或者没有结果。
    private struct Scoring
    {
        public string value;
        public int score;
    }
    
    private async Task FindOtherBrands(string term, 
                                       Dictionary<int, Scoring> results, 
                                       CancellationToken cancellationToken)
    {
        MySqlCommand cmd;
        string requete;
        requete = "SELECT m.id, m.nom as label 
                   FROM marque m 
                   WHERE m.nom LIKE '" + term + "%'";
    
        cmd = new MySqlCommand(requete, conn);
        var Rd = await cmd.ExecuteReaderAsync();
        while (Rd != null && await Rd.ReadAsync())
        {
            cancellationToken.ThrowIfCancellationRequested();
    
            int id = int.Parse(Rd["id"].ToString());
            if (!results.ContainsKey(id))
            {
                results.Add(id, new Scoring()
                {
                    score = 100,
                    value = Rd["label"].ToString()
                });
            }
        }
    
        Rd.Close();
        requete = "SELECT m.id, m.nom as label 
                   FROM marque m 
                   WHERE m.nom LIKE '%" + term + "%'";
    
        cmd = new MySqlCommand(requete, conn);
        Rd = await Cmd.ExecuteReaderAsync();
        while (Rd != null && await Rd.ReadAsync())
        {
            cancellationToken.ThrowIfCancellationRequest();
            int id = int.Parse(Rd["id"].ToString());
            if (!results.ContainsKey(id))
            {
                results.Add(id, new Scoring()
                {
                    score = 10,
                    value = Rd["label"].ToString()
                });
            }
        }
        Rd.Close();
    }
    
    private async Task<bool> RunWithTimeoutAsync(TimeSpan timeout)
    {
        bool finished;
        try
        {
            var cancellationTokenSource = new CancellationTokenSource(timeout);
            await FindOtherBrandsAsnyc(term, 
                  results, 
                  cancellationTokenSource.CancellationToken);
            finished = true;
        }
        catch (OperationCanceledException e)
        {
            // Handle
        }
    
        return finished;
    }