TPL任务和放大器;存储过程放大器、存储过程、任务、TPL

2023-09-06 19:23:25 作者:人心太凉、别太善良

我想知道是否可以使用任务,然后等待所有的结果返回给调用多个不同的存储过程使用相同的参数异步的。

我有以下几点:

 私人任务<数据表> DataBaseCall(字符串PROCEDURENAME,PARAMS对[]在这里)
    {
        数据表数据=新的DataTable();
        SqlConnection的连接=新的SqlConnection(connStr);

        SqlCommand的命令=新的SqlCommand(PROCEDURENAME,连接);
        connection.Open();

        的for(int i = 0; I< where.Length;我++)
        {
            command.Parameters.Add(其中,[I] .First.ToString(),其中[I] .Second.ToString());
        }

        VAR readerTask =任务< SqlDataReader的> .Factory.FromAsync(command.BeginExecuteReader,command.EndExecuteReader,NULL);
        返回readerTask.ContinueWith(T =>
            {
                VAR读卡器= t.Result;
                尝试
                {
                    reader.Read();
                    data.Load(读卡器);
                    返回的数据;
                }
                最后
                {
                    reader.Dispose();
                    command.Connection.Close();
                    command.Connection.Dispose();
                    command.Dispose();
                }
            });
    }
 

这点我有打电话:

 私人无效SetReportVariables(字符串所以reportName,字符串[] storedProcedureName,串_clientGroup,串_client code,串_finYear,串_period)
    {
       任务[]任务=新任务[storedProcedureName.Length]

        的for(int i = 0; I< storedProcedureName.Length;我++)
        {
            名单<对>参数=新的名单,其中,对>();
            parameters.Add(新配对(@ ClientGroup,_clientGroup));
            parameters.Add(新配对(@客户code,_client code));
            parameters.Add(新配对(@ FinYear,_finYear));

            任务[I] = DataBaseCall(storedProcedureName [I],parameters.ToArray());
        }
        Task.WaitAll(任务);

        ...........做一些与数据表.........
    }
 

我有三个问题。

谁能告诉我,如果这是一个好办法做到这一点? 在任何想法就是为什么我_finYear变量似乎有时会被忽略,这将导致一个错误。 我可以从任务返回的数据表?

感谢

迈克

解决方案 没有什么根本性错误使用这种方法。 您没有显示出在哪里_finYear来自于你的code,但基于code,你做的节目我看不出有任何理由为它无法通过正确地传递给存储过程。 当然,你可以返回数据表之类的。这不是安全地从多个线程同时访问,但它不能在线程交给没有问题。

在code上只有轻微的错误是,你应该有另一个try /终于在连续处理逻辑,因为有可能为 t.Result 抛出如果异常有一个与异步调用的问题开始/ EndExecuteReader 这将让你无法处置的指挥和连接。因此,这将是更好:

  readerTask.ContinueWith(T =>
{
    尝试
    {
        VAR读卡器= t.Result;

        尝试
        {
            reader.Read();
            data.Load(读卡器);

            返回的数据;
        }
        最后
        {
            reader.Dispose();
        }
    }
    最后
    {
        command.Connection.Close();
        command.Connection.Dispose();
        command.Dispose();
    }
});
 
使用高精度仪表放大器进行远程检测

I was wondering if it is possible to call a number of different stored procedures with the same parameters asynchronously by using tasks and then waiting for all the results to return.

I have the following:

private Task<DataTable> DataBaseCall(string procedureName, params Pair[] where)
    {
        DataTable data = new DataTable();
        SqlConnection connection = new SqlConnection(connStr);

        SqlCommand command = new SqlCommand(procedureName, connection);
        connection.Open();

        for (int i = 0; i < where.Length; i++)
        {
            command.Parameters.Add(where[i].First.ToString(), where[i].Second.ToString());
        }

        var readerTask = Task<SqlDataReader>.Factory.FromAsync(command.BeginExecuteReader, command.EndExecuteReader, null);
        return readerTask.ContinueWith(t =>
            {
                var reader = t.Result;
                try
                {
                    reader.Read();
                    data.Load(reader);
                    return data;
                }
                finally
                {
                    reader.Dispose();
                    command.Connection.Close();
                    command.Connection.Dispose();
                    command.Dispose();
                }
            });
    }

Which I call with:

private void SetReportVariables(string reportName, string[] storedProcedureName, string _clientGroup, string _clientCode, string _finYear, string _period)
    {
       Task[] tasks = new Task[storedProcedureName.Length];

        for (int i = 0; i < storedProcedureName.Length; i++)
        {
            List<Pair> parameters = new List<Pair>();
            parameters.Add(new Pair("@ClientGroup", _clientGroup));
            parameters.Add(new Pair("@ClientCode", _clientCode));
            parameters.Add(new Pair("@FinYear", _finYear));

            tasks[i] = DataBaseCall(storedProcedureName[i], parameters.ToArray());
        }
        Task.WaitAll(tasks);

        ...........Do something with the DataTables.........
    }

I have three questions.

Can anyone tell me if this is a good way to do this? Any idea's why my _finYear variable seems sometimes be omitted, which causes an error. Can I return the datatable from the task?

Thanks

Mike

解决方案

There's nothing fundamentally wrong with this approach. You don't show where _finYear comes from in your code, but based on the code that you do show I don't see any reason for it to not be passed through correctly to the sproc. Sure you can return the DataTable like that. It's not safe to access concurrently from multiple threads, but it can be handed across threads no problem.

The only minor bug in your code is that you should have another try/finally in your continuation handling logic because it's possible for t.Result to throw an exception if there was a problem with the async call to Begin/EndExecuteReader and that would leave you not disposing of the command and connection. So this would be better:

readerTask.ContinueWith(t =>            
{                
    try
    {
        var reader = t.Result;                

        try                
        {                    
            reader.Read();
            data.Load(reader);

            return data;
        }
        finally
        {                    
            reader.Dispose();
        }            
    }
    finally
    {
        command.Connection.Close();
        command.Connection.Dispose();
        command.Dispose();                
    }
});