我正在尝试多线程编码。如果我想并行执行进程。说,我想同时查询表A,表B和表C.在每个线程中,我正在执行SqlCommand .ExecuteReader,如下所示:
使用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())会导致对象引用未设置为对象异常错误的实例。知道如何使这个设置适用于多线程环境吗?
答案 0 :(得分:2)
Thread thread = new Thread(() =>
{
ST_Process stp = new ST_Process(connStr);
stp.OnRecordsFound += new GetStringArrayResult(getEvent);
stp.ListOfFieldValue(tf.TableName, tf.FieldName);
});
您需要先订阅该活动。此外,每当调用事件时,最好检查null:
var e = someEvent;
if (e != null)
e();
答案 1 :(得分:2)
写入数据的List<T>
类的实例方法(例如Add
)不是线程安全的,因此不能被多个线程使用。
线程安全
此类型的公共静态(在Visual Basic中为Shared)成员是线程安全的。不保证任何实例成员都是线程安全的。
在List上执行多个读取操作是安全的,但如果在读取集合时修改了集合,则可能会出现问题。要确保线程安全,请在读取或写入操作期间锁定集合。要使多个线程可以访问集合以进行读写,您必须实现自己的同步。对于具有内置同步的集合,请参阅System.Collections.Concurrent命名空间中的类。
相反,您应该使用线程安全的集合类,例如System.Collections.Concurrent.ConcurrentQueue
https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/
答案 2 :(得分:0)
快速浏览一下,假设您的三个线程正在同一个对象上调用ListOfFielfValue,看起来您的多个线程都写入共享变量_result。您需要在方法中将其设为局部变量。
跨线程共享对象。如果您希望多个线程访问同一个对象,那么您将需要某种锁定。