0

我在 backgroundworker 类中遇到了一些奇怪的行为,这让我相信我并不完全理解它是如何工作的。我假设以下代码部分或多或少是相同的,除了 BackgroundWorker 实现的一些额外功能(如进度报告等):

第 1 节:

    void StartSeparateThread(){
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.RunWorkerAsync();
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        //Execute some code asynchronous to the thread that owns the function
        //StartSeparateThread() but synchronous to itself.

        var SendCommand = "SomeCommandToSend";
        var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
        var SecondsToWait = 30;

        //this calls a function that sends the command over the NetworkStream and waits
        //for various responses.
        var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
    }

第 2 节:

    void StartSeparateThread(){
        Thread pollThread = new Thread(new ThreadStart(DoStuff));
        pollThread.Start();
    }

    void DoStuff(object sender, DoWorkEventArgs e)
    {
        //Execute some code asynchronous to the thread that owns the function
        //StartSeparateThread() but synchronous to itself.

        var SendCommand = "SomeCommandToSend";
        var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
        var SecondsToWait = 30;

        //this calls a function that sends the command over the NetworkStream and waits
        //for various responses.
        var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
    }

我正在使用第 1 节运行一些代码,这些代码通过网络流发送字符串并等待所需的响应字符串,并在此期间捕获所有输出。我编写了一个函数来执行此操作,该函数将返回网络流输出、发送字符串的索引以及所需响应字符串的索引。我看到了一些奇怪的行为,所以我将函数更改为仅在找到发送字符串和输出字符串时返回,并且找到的字符串的索引大于发送的字符串的索引。否则它将永远循环(仅用于测试)。我会发现该函数确实会返回,但两个字符串的索引都是 -1 并且输出字符串为空,或者有时填充了前一次调用的预期输出。如果我要猜测发生了什么,从 bw_DoWork() 函数内部调用的外部函数将与拥有 bw_DoWork() 函数的线程异步运行。结果,由于我的 SendAndWaitFor() 函数被连续多次调用。第二次调用将在第一次调用完成之前运行,在返回后覆盖第一次调用的结果,但在它们可以被评估之前。这似乎是有道理的,因为第一次调用总是会正确运行,而后续调用会显示上述奇怪的行为,但这似乎与 BackgroundWorker 类的行为方式背道而驰。此外,如果我在 SendAndWaitFor 函数内中断,事情会正常运行。这再次让我相信在 bwDoWork 函数本身中存在一些多线程。

当我将上面第一部分的代码更改为第二部分的代码时,事情完全按预期工作。那么,任何了解 BackgroundWorker 类的人都可以解释可能发生的情况吗?以下是一些可能相关的相关功能。

谢谢!

public Dictionary<string, string> SendAndWaitFor(string sendString, List<string> toWaitFor, int seconds)
    {
        var toReturn = new Dictionary<string, string>();
        var data = new List<byte>();
        var enc = new ASCIIEncoding();

        var output = "";
        var FoundString = "";

        //wait for current buffer to clear
        output = this.SynchronousRead();
        while(!string.IsNullOrEmpty(output)){
            output = SynchronousRead();
        }

        //output should be null at this point and the buffer should be clear.

        //send the desired data
        this.write(enc.GetBytes(sendString));

        //look for all desired strings until timeout is reached
        int sendIndex=-1;
        int foundIndex = -1;
        output += SynchronousRead();
        for (DateTime start = DateTime.Now; DateTime.Now - start < new TimeSpan(0, 0, seconds); )
        {
            //wait for a short period to allow the buffer to fill with new data              
            Thread.Sleep(300);

            //read the buffer and add it to the output
            output += SynchronousRead();
            foreach (var s in toWaitFor)
            {
                sendIndex = output.IndexOf(sendString);
                foundIndex = output.LastIndexOf(s);
                if (foundIndex>sendIndex)
                {
                    toReturn["sendIndex"] = sendIndex.ToString();
                    toReturn["foundIndex"] = sendIndex.ToString();
                    toReturn["Output"] = output;
                    toReturn["FoundString"] = s;
                    return toReturn;
                }
            }

        }
        //Set this to loop infinitely while debuging to make sure the function was only
        //returning above
        while(true){
        }
        toReturn["sendIndex"]="";
        toReturn["foundIndex"]="";
        toReturn["Output"] =output;
        toReturn["FoundString"] = "";
        return toReturn;
    }
    public void write(byte[] toWrite)
    {
        var enc = new ASCIIEncoding();
        var writeString = enc.GetString(toWrite);

        var ns = connection.GetStream();
        ns.Write(toWrite, 0, toWrite.Length);
    }

 public string SynchronousRead()
    {
        string toReturn = "";
        ASCIIEncoding enc = new ASCIIEncoding();
        var ns = connection.GetStream();

        var sb = new StringBuilder();
        while (ns.DataAvailable)
        {
            var buffer = new byte[4096];
            var numberOfBytesRead = ns.Read(buffer, 0, buffer.Length);
            sb.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, numberOfBytesRead));
            toReturn += sb.ToString();
         }         

        return toReturn;
    }
4

1 回答 1

1

后台工作人员要使用的所有数据都应通过 DoWorkEventArgs 传递,并且不应从类(或 GUI 界面)中提取任何内容。

在查看您的代码时,我无法确定创建属性(?)连接的位置。我的猜测是,连接是在不同的线程上创建的,或者可能是从 GUI(?)中提取读取信息,其中任何一个都可能导致问题。

我建议您在 dowork 事件中创建连接实例,而不是从不同的线程中拉出现有的连接实例。还要验证数据连接是否可以访问 GUI 之外的任何信息,但它的信息是按其制作的方式传入的。

我在我的博客C# WPF: Linq Fails in BackgroundWorker DoWork Event上讨论了后台工作人员的一个问题,这可能会告诉你问题出在你的代码中。

于 2012-12-12T19:22:49.927 回答