[英]How to use delegate and event in multi-threading c#
我正在试验多线程编码。 如果我想并行执行进程。 比如说,我想同时查询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()) 会导致对象引用未设置为对象异常错误的实例。 知道如何使此设置适用于多线程环境吗?
Thread thread = new Thread(() =>
{
ST_Process stp = new ST_Process(connStr);
stp.OnRecordsFound += new GetStringArrayResult(getEvent);
stp.ListOfFieldValue(tf.TableName, tf.FieldName);
});
您需要先订阅该事件。 此外,每当调用事件时,检查是否为空是一种很好的做法:
var e = someEvent;
if (e != null)
e();
写入数据的List<T>
类的实例方法(例如Add
)不是线程安全的,因此不能被多个线程使用。
线程安全
此类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。 不保证任何实例成员都是线程安全的。
对 List 执行多次读取操作是安全的,但如果在读取时修改集合,则可能会出现问题。 为确保线程安全,请在读取或写入操作期间锁定集合。 要使集合可以被多个线程访问以进行读写,您必须实现自己的同步。 有关具有内置同步的集合,请参阅 System.Collections.Concurrent 命名空间中的类。
相反,您应该使用线程安全的集合类,例如System.Collections.Concurrent.ConcurrentQueue
https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/
快速浏览一下,假设您的三个线程在同一个对象上调用 ListOfFielfValue,看起来您的多个线程都在写入共享变量 _result。 您需要在方法中将其设为局部变量。
对象是跨线程共享的。 如果您想让多个线程访问同一个对象,那么您将需要某种锁定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.