2017-10-25 5 views
-2

私はマルチスレッドコーディングを試しています。私が並行してプロセスを実行したい場合。テーブルAとテーブルBとテーブルCを同時に照会したいとします。各スレッド内で、次のようにSqlCommand .ExecuteReaderを実行しています。マルチスレッドでのデリゲートとイベントの使い方C#

using System.Collections.Generic; System.Data.SqlClientを使用している ;

namespace MultiThread2 
{ 
    public class TableFieldArray 
    { 
     public string TableName { get; set; } 
     public string FieldName { get; set; } 
    } 

    static class MTProcess 
    { 
     const string connStr = "server=***;database=***;user id=***;password=***"; 

     public static event GetStringArrayResult OnRecordsFoundMTS; 

     public static void GetStringArrays(TableFieldArray[] tableFieldArray) 
     { 
      foreach (TableFieldArray tf in tableFieldArray) 
      { 
       Thread thread = new Thread(() => 
       { 
        ST_Process stp = new ST_Process(connStr); 
        stp.ListOfFieldValue(tf.TableName, tf.FieldName); 
        stp.OnRecordsFound += new GetStringArrayResult(getEvent); 
       }); 

       thread.Start(); 
      } 
     } 

     private static void getEvent(string[] result) 
     { 
      OnRecordsFoundMTS(result); 
      return; 
     } 

// --------------- 

    public delegate void GetStringArrayResult(string[] output); 

    class ST_Process 
    { 
     public event GetStringArrayResult OnRecordsFound; 

     private readonly string _connStr; 

     public ST_Process(string connectionString) 
     { 
      _connStr = connectionString; 
     } 

     public void ListOfFieldValue(string tableName, string fieldName) 
     { 
      List<string> result = new List<string>(); 
      using (SqlConnection sqlConn = new SqlConnection(_connStr)) 
      { 
       sqlConn.Open(); 
       string sqlText = string.Format("SELECT TOP 100 {0} FROM {1} ", fieldName, tableName); 
       using (SqlCommand sqlcmd = new SqlCommand(sqlText, sqlConn) { CommandType = System.Data.CommandType.Text }) 
       { 
        var r = sqlcmd.ExecuteReader(); 
        while (r.Read()) 
        { 
         result.Add(r[fieldName].ToString()); 
        } 
        OnRecordsFound(_result.ToArray()); 
       } 
       sqlConn.Close(); 
      } 
     } 
    } 
} 

別個のスレッドで実行されたときに問題があり、OnRecordsFound(_result.ToArrayは())オブジェクト参照がオブジェクトの例外エラーのインスタンスに設定されていない原因となります。どのようにマルチスレッド環境でこの設定を行うにはどのようなアイデア?

+3

ようなスレッドセーフなコレクションクラスを使用する必要があります。それらのスレッドのどれかがリストを変更した場合は、 'lock()'を使ってそのアクセスを管理する必要があります。 – Amy

+0

(ListOfFieldValueへの複数の呼び出しで)呼び出しコードを含めてください。 – mjwills

+0

私はちょうど元の投稿に貼り付けました。Amy – Yogi

答えて

0

3つのスレッドが同じオブジェクト上でListOfFielfValueを呼び出すと仮定すると、複数のスレッドがすべて共有変数_resultに書き込みを行っているように見えます。これをメソッドのローカル変数にする必要があります。

オブジェクトはスレッド間で共有されます。複数のスレッドが同じオブジェクトにアクセスしたい場合は、何らかのロックが必要です。

+0

Amyとsjbありがとうございました、私は_resultをメソッドの内外に移動していますが、最初のイベントOnRecordsFoundで動作しますが、2回目のOnRecordsFoundイベントがトリガされ、_resultがあってもオブジェクト参照に関して不平を言います。私がそれをメソッドに入れると結果が正しいデータで埋められます。私は結果が失われたと思って申し訳ありませんが、明らかにイベントの考えの結果が失われた、またはそのようなものの何か。 – Yogi

2
Thread thread = new Thread(() => 
{ 
    ST_Process stp = new ST_Process(connStr); 
    stp.OnRecordsFound += new GetStringArrayResult(getEvent); 
    stp.ListOfFieldValue(tf.TableName, tf.FieldName); 
}); 

イベントを最初に購読する必要があります。イベントを呼び出したときにも、nullをチェックすることをお勧めです。(例えばAddなど)のデータを書き込むList<T>クラスの

var e = someEvent; 
if (e != null) 
    e(); 
+0

これはうまくいくので、問題は各スレッドでプロセスが順次ではなく、実際のメソッド呼び出し(この場合はListOfFieldValue)に先行する必要がある+ =です。そして、はい、私はそれが良い習慣になることに同意します。 Zer0ありがとう! – Yogi

2

インスタンスメソッドはスレッドセーフではありませんので、複数のスレッドで使用することはできません。 (Visual BasicではShared)

https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.1#Thread_Safety

スレッドセーフ

この型のpublic staticメンバは、スレッドセーフです。どのインスタンスメンバーもスレッドセーフであるとは限りません。

Listに対して複数の読み取り操作を実行することは安全ですが、読み取り中にコレクションが変更されると問題が発生する可能性があります。スレッドの安全を確保するには、読み取り操作または書き込み操作中にコレクションをロックします。読み取りと書き込みのために複数のスレッドがコレクションにアクセスできるようにするには、独自の同期を実装する必要があります。組み込み同期を使用するコレクションについては、System.Collections.Concurrent名前空間のクラスを参照してください。

代わりに、あなたはあなたがスレッド間の `一覧 `を共有している System.Collections.Concurrent.ConcurrentQueue

https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/

関連する問題