繁体   English   中英

可以并行处理非线程安全代码吗?

[英]Possible to process non-thread-safe code in parallel?

我有一个在DataRows上执行foreach的C#程序。 简化版为:

foreach (DataRow row in masterReportData.Rows)
{
    LoopThruReports(row, p2, p3, p4);
}

LoopThruReports声明为:

public static void LoopThruReports(DataRow row, DataTable p2, string p3, string p4)

我真的很想让迭代并行运行,因为它们从数据库中填充了SqlDataAdapter (这需要一段时间)。 尽管尝试了线程,任务和并行库,但我什至无法确定这样做是否可行。 如果只能让每个线程不与其他线程共享变量! 是否有人对这是否可能有任何想法,如果可以,我该怎么做? 谢谢。

编辑: LoopThruReports方法调用GetGCHAReportDetailData ,这是Fill地方。 这是这种方法的样子:

    public static DataSet GetGCHAReportDetailData(int reinsuranceContract, int billingMode, DateTime reportPeriod)
    {
        DataSet dsResults = new DataSet();
        DataTable gchaReportData = new DataTable();

        string connStr = ConfigurationManager.ConnectionStrings["UtopiaConnString"].ConnectionString;

        using (SqlCommand sqlCmd = new SqlCommand("dbo.[sproc_GCHADetailDataForContract]"))
        {
            sqlCmd.CommandType = CommandType.StoredProcedure;

            //set the command time out to 60 minutes b/c this report takes time to generate
            sqlCmd.CommandTimeout = 0;

            SqlParameter parm = new SqlParameter("@ReinsuranceContractId", SqlDbType.Int);
            parm.Value = reinsuranceContract;
            sqlCmd.Parameters.Add(parm);

            SqlParameter parm1 = new SqlParameter("@BillingMode", SqlDbType.Int);
            parm1.Value = billingMode;
            sqlCmd.Parameters.Add(parm1);

            SqlParameter parm2 = new SqlParameter("@ReportingPeriod", SqlDbType.DateTime);
            parm2.Value = reportPeriod;
            sqlCmd.Parameters.Add(parm2);
            sqlCmd.CommandTimeout = 0;

            using (SqlConnection conn = new SqlConnection(connStr))
            {
                sqlCmd.Connection = conn;

                using (SqlDataAdapter da = new SqlDataAdapter(sqlCmd))
                {
                    try
                    {
                        conn.Open();
                        da.Fill(dsResults);

                        conn.Close();
                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex, "Error retrieving data from the database.\r\n Message: {0}", ex.Message);
                        throw;
                    }
                    finally
                    {
                        if (conn.State != ConnectionState.Closed)
                            conn.Close();
                    }
                }
            }
        }

        return dsResults;
    }

尝试对同一SQL Server的多个Fill调用进行多线程将是一场艰苦的战斗。 SQL Server和网络都已经尽可能快地向您发送数据。 通过将Fill调用划分为多个线程,您实际上会减慢速度。 带宽将在每个线程之间平均分配(因此不会提高性能),除了每个线程也会增加一些开销,这会降低速度。 您添加的每个线程都会使这个问题更加复杂。

如果每个Fill在不同的数据库上运行,那么您可能会在线程方面有所改进,因为尽管网络争用仍然是一个问题,但它们不会竞争相同的SQL资源。

我在您的代码中看不到任何会使多线程不安全的东西,只是看不到对其进行线程处理将如何改善您的处理时间。


注释表明您实际上是根据数据生成报告,并且这就是您要进行多线程处理的内容。 这可能实际上可行。 只要报告仅读取数据,而不写入数据,您就应该能够对它们进行线程化,并共享单个数据集。

根据MSDN的说明DataTablesDataSet具有此注释“此类型对于多线程读取操作是安全的。您必须同步任何写入操作。”

它应该是完全正常的共享同一个DataSet只要线程之间例如因为你只从它都需要进行特殊处理。

这段代码似乎有效:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ParallelDBProcessing
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Task> taskList = new List<Task>();
            for (int i = 1; i <= 4; i++)
            {
                int temp = i;
                Task task = Task.Run(() => DoWork(temp));   // use Task.Run if you want to get task.Result back
                taskList.Add(task);
            }
            Task.WaitAll(taskList.ToArray());
        }

        public static void DoWork(int num)
        {
            Console.WriteLine(num);
            string connstr = @"Data Source = (local)\sqlexpress; Initial Catalog = Barry; Integrated Security = true";

            DataSet dsResults = new DataSet();
            using (SqlCommand sqlCmd = new SqlCommand("dbo.[spMagicProc]"))
            {
                sqlCmd.CommandType = CommandType.StoredProcedure;

                SqlParameter parm = new SqlParameter("@Input_A", SqlDbType.Int);
                parm.Value = num;
                sqlCmd.Parameters.Add(parm);

                SqlParameter parm1 = new SqlParameter("@Input_B", SqlDbType.Int);
                parm1.Value = 2;
                sqlCmd.Parameters.Add(parm1);

                using (SqlConnection conn = new SqlConnection(connstr))
                {
                    sqlCmd.Connection = conn;

                    using (SqlDataAdapter da = new SqlDataAdapter(sqlCmd))
                    {
                        try
                        {
                            conn.Open();
                            da.Fill(dsResults);
                            conn.Close();
                        }
                        catch (Exception ex)
                        {
                            throw;
                        }
                        finally
                        {
                            if (conn.State != ConnectionState.Closed)
                                conn.Close();
                        }
                    }

                }
                Console.WriteLine(dsResults.Tables[0].Rows.Count);
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM