簡體   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