繁体   English   中英

如何在多线程 C# 中使用委托和事件

[英]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 )不是线程安全的,因此不能被多个线程使用。

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

线程安全

此类型的公共静态(在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM