2013-05-13 49 views
1

我這裏有一個函數調用多個線程另一個函數。在第二個功能,refValuesd [,]的每個元素的值是1。但是,當我檢查相同的2D陣列的元件在多個線程被稱爲後graph1()函數,我得到不同的值的元素refValuesd [,]。我是多線程的新手。不執行多線程正確

void graph1() 
    { 
     for (int j = 0; j < 366; j++) //loop to refresh element values 
     { 
      refValues[j] = 0; 
      threshValues[j] = 0; 
      y_Values[j] = 0; 
      y__Values[j] = 0; 
      yValues[j] = 0; 
      for (int k = 0; k < 1000; k++) 
      { 
       threshValuesd[k,j] = 0; 
       refValuesd[k,j] = 0; 
       y__Valuesd[ k,j] = 0; 
       y_Valuesd[k,j] = 0; 
       yValuesd[k,j] = 0; 
      } 

     } 

     List<string>[] list = new List<string>[4];//manpower details 
     list = A.mandetselect(); 
     int number = A.Countmandet();//retuns an integer value 
     string[] trade = list[1].ToArray(); 
     string[] license = list[2].ToArray(); 
     string[] training = list[3].ToArray(); 
     string[] display_status = list[4].ToArray(); 

     List<string>[] listc = new List<string>[14];//Project details 
     listc = A.Select(); 
     int numberc = A.Count(); 

     string abc = ""; 
     int q = 0; 
     for (int j = 0; j < number; j++) 
     { 

      if (!display_status[j].Equals("NO") && (selection == "ALL" || (selection == "ALL-LAE" && license[j] != "") || (selection == "ALL-NON LAE" && license[j] == "") || (selection == "AVIONICS -ALL" && trade[j] == "Avionics") || (selection == "AVIONICS-NON LAE" && trade[j] == "Avionics" && license[j] == "") || (selection == "AVIONICS-LAE" && trade[j] == "Avionics" && license[j] != "") || (selection == "AIRFRAME-ALL" && trade[j] == "Airframes") || (selection == "AIRFRAME-NON LAE" && trade[j] == "Airframes" && license[j] == "") || (selection == "AIRFRAME-LAE" && trade[j] == "Airframes" && license[j] != ""))) 
      { 
       int z = numberc; 
       string[] nameofproj = listc[0].ToArray(); 
       int copy = q; 
       int copy2 = j; 
       string a = abc; 
       string[] name = list[0].ToArray(); 
       List<string>[] lista = new List<string>[5]; 
       string[] status = listc[13].ToArray(); 
       thread[copy] = new Thread(() => graph1threader(a, name[copy2], lista, z, nameofproj, status, copy)); 
       thread[copy].Start(); 

       q++; 

      } 
     } 


     for (int j = 0; j < 366; j++) 
     { 
      for (int k = 0; k < q; k++) 
      { 
       threshValues[j] += threshValuesd[k, j];     
       refValues[j] += refValuesd[k, j]; 
       y__Values[j] += y__Valuesd[k, j]; 
       y_Values[j] += y_Valuesd[k, j]; 
       yValues[j] += yValuesd[k, j]; 
      } 
     } 


      for (int j = 0; j < 366; j++) 
      { 
       DateTime temp = G.AddDays(j); 
       string temper = temp.ToShortDateString(); 
       y__Values[j] = y__Values[j] - y_Values[j]; 
       xNames[j] = temper; 
      } 

     chart1.Series[1].Points.DataBindXY(xNames, y_Values);   


     chart1.Series[2].Points.DataBindXY(xNames, y__Values); 


     chart1.Series[3].Points.DataBindXY(xNames, refValues); 


     chart1.Series[4].Points.DataBindXY(xNames, threshValues); 


     chart1.Series[5].Points.DataBindXY(xNames, yValues); 



    } 

這裏是在多線程執行的功能:

void graph1threader(string abc,string nameofj,List<string>[] lista,int numberc,string[] nameofproj,string[] status,int copy) 
    { 
     DBConnect A = new DBConnect(); 
     int x = copy; 
     string[] projname; 
     string[] country; 
     string[] start; 
     string[] end; 
     abc = nameofj.Replace(" ", "_"); 
     lista = A.manprojselect(abc); 
     projname = lista[0].ToArray(); 
     country = lista[2].ToArray(); 
     start = lista[3].ToArray(); 
     end = lista[4].ToArray(); 

     for (int k = 0; k < 366; k++)//basic 
     { 

      refValuesd[x, k]++; 
      refValuesd[copy,k].ToString()); 
      threshValuesd[x, k] = 0.8; 
      string Status = ""; 
      int flag = 0; 

      for (int l = 0; l < A.Countproj(abc); l++) 
      { 

       for (int m = 0; m < numberc; m++) 
       { 
        if (nameofproj[m] == projname[l]) 
        { 
         Status = status[m]; 
        } 
       } 


       DateTime shuru = DateTime.ParseExact(start[l], 
           "dd-MM-yyyy hh:mm:ss", 
           CultureInfo.InvariantCulture); 
       DateTime anth = DateTime.ParseExact(end[l], 
           "dd-MM-yyyy hh:mm:ss", 
           CultureInfo.InvariantCulture); 
       if (temp >= shuru && temp <= anth) 
       { 
        if (Status != "PLANNED" && Status != "LO" && flag == 0) 
        { 
         y_Valuesd[x,k]++;//BASIC UTILISATION 
         flag = 1; 
        } 
        if (Status == "IP" || Status == "OTD") 
         y__Valuesd[x,k]++;//EXCESS 
        if (Status == "PLANNED") 
        { 
         yValuesd[x,k]++;//UNUTILISED 

        } 

       } 

      } 


     } 

    } 
+3

閱讀多線程的一些教程。它需要鎖定,同步和保護。你的代碼不是線程安全的。 – 2013-05-13 13:26:30

+1

看起來像一個給我的報告 - 你想要多線程報告處理?是否有原因(性能?) – Charleh 2013-05-13 13:26:41

+0

是的我需要多線程以減少執行時間。它是實時報告生成,變量refValues,threshValues等是全局變量。 – 2013-05-13 13:28:53

回答

2

確保您圍繞接入與lock部分以refvalued,你會在正確的方向開始。你必須設計你的鎖定,不要造成競爭條件和死鎖。

編輯:因此,在審查你的代碼,這裏有一些意見

看起來你揭開序幕的圖形功能,進而執行多個線程的graph1threader功能。我不會質疑這是否是必要的 - 假設是你已經確定它是。事件

訂購

它看起來像你不會停下來等待所有的線程graph1threader繼續在你的第二個循環之前完成。所以這是一個問題:

  • 你想要的graph1threader作業,然後再繼續完成?

如果是這樣,你看過Task?而不是創建線程,你幾乎可以從字面上交換ThreadTask然後,一旦你已經完成了所有的任務對象的創建和啓動它們,你可以把Task.WaitAllfor (int j = 0; j < number; j++)循環之後等待它們完成,那麼你做之前第三for迴路graph1

 thread[copy] = new Task(() => graph1threader(a, name[copy2], lista, z, nameofproj, status, copy)); 
     thread[copy].Start(); 
     q++; 
    } 
} 
Task.WaitAll(thread); 

for (int j = 0; j < 366; j++) 
{ 
    for (int k = 0; k < q; k++) 
    { 
     threshValues[j] += threshValuesd[k, j]; 
     refValues[j] += refValuesd[k, j]; 
     y__Values[j] += y__Valuesd[k, j]; 
     y_Values[j] += y_Valuesd[k, j]; 
     yValues[j] += yValuesd[k, j]; 
    } 
} 

如果你不想使用Task(或不能)Thread.Join也將工作,但Task支持取消比Thread容易,所以如果你有一個長期運行的操作的UI它會讓你更容易。

共享領域

以下變量由兩個功能使用(請忽略任何不正確的變量類型,它是非常重要的名字):

double[,] threshValuesd; 
int[,] refValuesd; 
int[,] y__Valuesd; 
int[,] y_Valuesd; 
int[,] yValuesd; 

我打電話這個名單書籤甲供以後使用

所有這些潛在的需要保護免受多線程race conditions

如何保護共享領域

不管你是否等待與否,你需要保護你的共享字段graph1threader。所以,如果我正確讀取您的代碼:

  • 你通過一個獨特的,增加值graph1threader
  • 因此(在graph1你的第二for循環使用q++)以下行不會競相設定值線程在graph1threader之間:
    • refValuesd[x, k]++;
    • threshValuesd[x, k] = 0.8;
    • y_Valuesd[x,k]++;
    • y__Valuesd[x,k]++;
    • yValuesd[x,k]++;

邊注:這些變量名都不能理解我,你可以嘗試在一個更具描述性的方式命名他們比yValuesdy_Valuesdy__Valuesd到幫助你在後面調試)。

但是,即使線程不競爭更新同一陣列插槽中的值,您可能會遇到內存障礙形式和對單個陣列插槽的讀/寫訪問問題。因此,我建議這樣做,很簡單聲明一個類領域:

private readonly object SyncRoot = new object(); 

再繞所有訪問任何我上面書籤提到的共享領域的一個你需要使用的(選擇你的第一個循環爲例):

lock (this.SyncRoot) { 
    for (int j = 0; j < 366; j++) 
    { 
     for (int k = 0; k < q; k++) 
     { 
      threshValues[j] += threshValuesd[k, j]; 
      refValues[j] += refValuesd[k, j]; 
      y__Values[j] += y__Valuesd[k, j]; 
      y_Values[j] += y_Valuesd[k, j]; 
      yValues[j] += yValuesd[k, j]; 
     } 
    } 
} 

保持鎖定呼叫的頻率儘可能不頻繁,但儘可能接近共享資源。我的意思是如果你願意的話,你可以鎖定內部的for循環,但是這樣做會比較慢,但是你可能需要這樣做,以便讓其他線程在鎖定同一個對象時更頻繁地進行。

注:此使用共享鎖的技術假設你想要的graph1threader線程在同一時間,你的第三個for環路graph1運行(不要求即我的評論大約Task對象)。如果情況並非如此,我認爲你可以在每個函數中創建一個本地對象,然後鎖定它。每個線程因此將具有不同的鎖定對象。因爲沒有線程同時訪問陣列中的同一個插槽,這隻會強制實現內存屏障,並確保所有線程在讀取它們時都看到相同的值。

對不起,這是如此之久,很難知道從哪裏開始,不知道更多關於您通過構建此代碼的假設。

+0

+1不錯,詳細的答案。 – 2013-05-13 14:19:49

2

有可能在那裏的幾個問題,而是兩個我可以發現:

首先,線程正在試圖做的refValuesd[x, k]++這是不是線程安全的。

試試這個:

Interlocked.Increment(ref refValuesd[x, k]); 

其次,你是不是在等待所有線程使用他們已經生成的數據之前終止。嘗試只是for (int j = 0; j < 366; j++)行之前添加此:

foreach (var thread in threads) 
    thread.Join(); 

它看起來就像你有很多東西要學,所以我建議你閱讀本免費電子書:

http://www.albahari.com/threading/

+0

翻看代碼我不確定Interlocked實際上是需要的,因爲代碼看起來是分區的(假設這些線程應該在'graph'中再次使用之前完成),儘管它至少會強化內存障礙 - 但是你也會需要在主線程上再次讀取這些值。如果可能的話,我還建議使用任務而不是線程,因爲如果需要的話,它會使取消更容易。加一個albahari參考。 – 2013-05-13 14:02:31

+1

@AndyBrown看起來你可能是對的 - 'x'是'copy'參數的副本,它看起來是對數據進行分區。我實際上認爲應該使用'Parallel.For'或其他一些TPL功能來取消和重寫整個內容,但是我擔心這將超出OP的當前知識。 – 2013-05-13 14:15:37

+0

我完全贊同。在這種情況下,我喜歡['Parallel.For'](http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.for.aspx)的想法幾乎比'Task'好只要這些線程在繼續之前完成)。 Albahari是一個開始學習的好地方 - 很好被發現。 – 2013-05-13 14:35:40