多线程:在线程之间传递不同的自定义控件属性(或异步使用它们)

时间:2014-06-24 18:01:30

标签: c# winforms

我正在尝试开发一个项目,我可以让多个客户端为压力测试提出自己的服务器请求。当我创建一个新线程并希望该线程完成工作时,我很难弄清楚如何操作自定义控件属性。我有超过100个控件;理想情况下,100个客户。问题是我的控件是GUI的一部分,我不知道如何允许有问题的线程从该相对控件获得访问权。

以下是我所拥有的:

    // Custom project.class to get access to a custom base of properties created within the tool itself.
        List<custom_control_project.custom_control_widget> controlList = new List<custom_control_project.custom_control_widget>();

    private async void btnStart_Click(object sender, EventArgs e)
        {
    ...// control property initializations

    foreach (var control in controlList)
                {
                    if (control.Enabled)
                    {
                        Thread thread = new Thread(() => StartClient());
                        thread.Start();

                        // Loop until worker thread activates.
                        while (!thread.IsAlive);

                        ... // Ideally the GUI updates would happen from these threads. Simple updates to labels based on status code responses and expected xml parameters received.
                    }
                 }

我的StartClient()主要基于Microsoft的异步套接字客户端示例:http://msdn.microsoft.com/en-us/library/bew39x2a%28v=vs.110%29.aspx

我是异步运行这些客户端但程序不是我的最终结果。我做了一些更改,包括重置ManualResetEvents。但是,当我运行我的应用程序时,所有控件仍然依次运行,我希望它们是独立的。通过使用StartClient()创建新线程,我有正确的方法吗?

参考microsofts示例,我最感兴趣的部分是ReceiveCallback(IAsyncResult ar)方法:

public class StateObject
    {
        // Client socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder sb = new StringBuilder();
    }

        // ManualResetEvent instances signal completion.
        private ManualResetEvent connectDone =
            new ManualResetEvent(false);
        private ManualResetEvent sendDone =
            new ManualResetEvent(false);
        private ManualResetEvent receiveDone =
            new ManualResetEvent(false);

        // The response from the remote device.
        private String response = String.Empty;

        public void StartClient()
        {
            // Connect to a remote device.
            try
            {
                // Establish the remote endpoint for the socket.
                // The name of the
                // remote device is "host.contoso.com".
                //IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
                //IPAddress ipAddress = ipHostInfo.AddressList[0];
                //IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP socket.
                Socket client = new Socket(AddressFamily.InterNetwork,
                                           SocketType.Stream, ProtocolType.Tcp);

                // Connect to the remote endpoint.
                client.BeginConnect(asyncServerHolder, asyncPortHolder,
                                    new AsyncCallback(ConnectCallback), client);
                connectDone.WaitOne();

                // Send test data to the remote device.
                Send(client, (Upload)); //POST HTTP string + xml parameters
                sendDone.WaitOne();

                // Receive the response from the remote device.
                Receive(client);
                receiveDone.WaitOne();

                // Write the response to the console.
                //MessageBox.Show("Response received : " + response);

                // Release the socket.
                client.Shutdown(SocketShutdown.Both);
                client.Close();

                // Reset all manual events for next instance.
                connectDone.Reset();
                sendDone.Reset();
                receiveDone.Reset();
            }
            catch (Exception)
            {
                //MessageBox.Show(e.ToString());
            }
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete the connection.
                client.EndConnect(ar);

                //MessageBox.Show("Socket connected to " + client.RemoteEndPoint.ToString());

                // Signal that the connection has been made.
                connectDone.Set();
            }
            catch (Exception)
            {
                //MessageBox.Show(e.ToString());
            }
        }

        private void Receive(Socket client)
        {
            try
            {
                // Create the state object.
                StateObject state = new StateObject();
                state.workSocket = client;

                // Begin receiving the data from the remote device.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception)
            {
                //MessageBox.Show(e.ToString());
            }
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the state object and the client socket
                // from the asynchronous state object.
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;

                // Read data from the remote device.
                int bytesRead = client.EndReceive(ar);

                if (bytesRead > 0)
                {
                    // There might be more data, so store the data received so far.
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                    // Get the rest of the data.
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                        new AsyncCallback(ReceiveCallback), state);
                }
                else
                {
                    // All the data has arrived; put it in response.
                    if (state.sb.Length > 1)
                    {
                        response1 = state.sb.ToString();
                    ///
                    ///
                    ///     **THIS IS WHERE I WANT ALL THE PROCESSING TO BE DONE**
                    ///     **AFTER THE RESPONSE IS COMPLETE!!**
                    ///
                    ///

                    }
                    // Signal that all bytes have been received.
                    receiveDone.Set();
                }
            }
            catch (Exception)
            {
                //MessageBox.Show(e.ToString());
            }
        }

        private void Send(Socket client, String data)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.
            client.BeginSend(byteData, 0, byteData.Length, 0,
                             new AsyncCallback(SendCallback), client);
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.
                int bytesSent = client.EndSend(ar);
                //MessageBox.Show("Sent " + bytesSent + " bytes to server.");

                // Signal that all bytes have been sent.
                sendDone.Set();
            }
            catch (Exception)
            {
                //MessageBox.Show(e.ToString());
            }
        }

我如何从传递给方法的foreach语句中获取相对控制,然后根据结果修改我的GUI(以某种方式调用?)?此外,是否可以使每个客户端独立,并且同时具有多个客户端到服务器到客户端请求?这是我的主要目标。

如果这是一个牵强附会或非常沮丧的尝试方法,请说出来。如果不考虑太多的编程术语(为了理解目的,我对编程很新),你会怎么做呢?

提前致谢!

1 个答案:

答案 0 :(得分:0)

好吧,你可以排序通过使用控件上的BeginInvoke函数来操纵来自不同线程的任何控件,传递你想要执行的操作。该操作将在UI线程上执行。

但是你的主要问题是你没有separate the concerns。您可以确定UI代码可以使操作发生,但这些操作应该与任何UI代码不同。您应该以这样的方式设计您的操作以使它们可重用。换句话说,如果您决定从头开始重写UI,您仍然可以按原样重用您的操作。

为了实现这一点,您的操作不应该引用任何UI,他们甚至不应该知道任何UI的存在。这将使您的代码更易于管理。因此,在不同的类中提取所有内容,然后使用类似事件的内容与UI进行通信。然后,UI代码将进行BeginInvoke调用。