.net 7 api 线程中的此 AsyncCallback 委托示例如何安全

How is this AsyncCallback delegate example from the .net 7 api thread safe

提问人:fskj 提问时间:8/2/2023 更新时间:8/2/2023 访问量:43

问:

在 AsyncCallback 委托的参考说明中,以下代码片段作为异步操作结束时如何执行回调的示例:

/*
The following example demonstrates using asynchronous methods to
get Domain Name System information for the specified host computers.
This example uses a delegate to obtain the results of each asynchronous
operation.
*/

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Specialized;
using System.Collections;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class UseDelegateForAsyncCallback
    {
        static int requestCounter;
        static ArrayList hostData = new ArrayList();
        static StringCollection hostNames = new StringCollection();
        static void UpdateUserInterface()
        {
            // Print a message to indicate that the application
            // is still working on the remaining requests.
            Console.WriteLine("{0} requests remaining.", requestCounter);
        }
        public static void Main()
        {
            // Create the delegate that will process the results of the
            // asynchronous request.
            AsyncCallback callBack = new AsyncCallback(ProcessDnsInformation);
            string host;
            do
            {
                Console.Write(" Enter the name of a host computer or <enter> to finish: ");
                host = Console.ReadLine();
                if (host.Length > 0)
                {
                    // Increment the request counter in a thread safe manner.
                    Interlocked.Increment(ref requestCounter);
                    // Start the asynchronous request for DNS information.
                    Dns.BeginGetHostEntry(host, callBack, host);
                 }
            } while (host.Length > 0);
            // The user has entered all of the host names for lookup.
            // Now wait until the threads complete.
            while (requestCounter > 0)
            {
                UpdateUserInterface();
            }
            // Display the results.
            for (int i = 0; i< hostNames.Count; i++)
            {
                object data = hostData [i];
                string message = data as string;
                // A SocketException was thrown.
                if (message != null)
                {
                    Console.WriteLine("Request for {0} returned message: {1}",
                        hostNames[i], message);
                    continue;
                }
                // Get the results.
                IPHostEntry h = (IPHostEntry) data;
                string[] aliases = h.Aliases;
                IPAddress[] addresses = h.AddressList;
                if (aliases.Length > 0)
                {
                    Console.WriteLine("Aliases for {0}", hostNames[i]);
                    for (int j = 0; j < aliases.Length; j++)
                    {
                        Console.WriteLine("{0}", aliases[j]);
                    }
                }
                if (addresses.Length > 0)
                {
                    Console.WriteLine("Addresses for {0}", hostNames[i]);
                    for (int k = 0; k < addresses.Length; k++)
                    {
                        Console.WriteLine("{0}",addresses[k].ToString());
                    }
                }
            }
       }

        // The following method is called when each asynchronous operation completes.
        static void ProcessDnsInformation(IAsyncResult result)
        {
            string hostName = (string) result.AsyncState;
            hostNames.Add(hostName);
            try
            {
                // Get the results.
                IPHostEntry host = Dns.EndGetHostEntry(result);
                hostData.Add(host);
            }
            // Store the exception message.
            catch (SocketException e)
            {
                hostData.Add(e.Message);
            }
            finally
            {
                // Decrement the request counter in a thread-safe manner.
                Interlocked.Decrement(ref requestCounter);
            }
        }
    }
}

我不明白该方法是如何实现线程安全的:如何不能同时执行多个并发回调?我可以看到竞争条件的风险很低,因为输入是由用户提供的,但这是唯一阻止多个并发执行的因素吗?ProcessDnsInformationhostNames.Add(hostName)hostNames.Add(hostName)

C# 异步 AsyncCallback

评论

2赞 canton7 8/2/2023
我认为你是对的。这个例子很古老(看看它使用的类型:和 -- 这是来自 C# 1),无论如何,MSDN 示例的质量都可能有问题。最重要的是,APM 模式 () 已经存在了 10+ 年:一切都使用 Tasks 和 TPL。用。ArrayListStringCollectionIAsyncResultDns.GetHostEntryAsync

答:

1赞 JonasH 8/2/2023 #1

异步并不意味着并发。可能永远不会执行任何并发回调,这应该是安全的,至少在集合方面是这样。BeginGetHostEntry

但是我看不出文档以一种或另一种方式指定了这一点,如果没有明确记录,您应该假设最悲观的观点,即需要锁。所以,是的,这个例子可能不是线程安全的,至少是一个好的线程安全实践的糟糕例子。

综上所述,这个模式和整个示例现在已经过时了,任何现代代码都应该使用 Tasks 和 async/await。这应该使这样的示例更易于阅读,并降低线程问题的风险。