C#背景工人附加文本框

时间:2018-08-08 22:17:01

标签: c#

首先,我想说我是C#的新手,所以我不太了解后台工作程序应该如何实现。我有一个GUI程序,该程序基本上可以ping域,并将响应返回到文本框。我能够使其正常工作,但是它冻结了代码,因为它在同一线程上运行,这就是为什么我尝试实现后台工作程序的原因。 这是基本设置

private void button1_Click(object sender, EventArgs e)
{
    url = textBox1.Text;
    button1.Enabled = false;
    button2.Enabled = true;
    bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
    bgWorker.RunWorkerAsync();

}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
    do
    {
        if (bgWorker.CancellationPending)
            break;

        Invoke((MethodInvoker)delegate { monitor(); });

    } while (true);
}
public void monitor()
{
    textBox2.AppendText("Status of: " + url + "\n");
    Status(url);
    System.Threading.Thread.Sleep(30000);
}
private void Status(string url)
{
    // This method does all the ping work and also appends the status to the Text box as it goes through , as OK or down
}

我以前从未与bgworkers一起工作过,并且您可以想象它令人困惑。我看了很多其他文章,但似乎无法理解。抱歉,如果代码看起来很疯狂,我正在尝试学习。

5 个答案:

答案 0 :(得分:1)

您有几个错误。首先,

Invoke((MethodInvoker)delegate
{
    monitor();
});

将在您的UI线程上调用monitor()。在几乎所有情况下,都不应该在其他线程上调用方法。您尤其不应调用在UI线程上阻塞或执行超过几毫秒的事情的方法,这就是这样做的原因:

System.Threading.Thread.Sleep(30000);

而不是在另一个线程上调用方法;向另一个线程提交不可变数据,然后让该线程决定何时处理它。 BackgroundWorker中已经内置了一个事件。在致电bgWorker.RunWorkerAsync()之前,请执行以下操作:

url = new Uri(something);
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.ProgressChanged += Bgw_ProgressChanged;


private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    textBox2.AppendText("Status of: " + url + ": " + e.UserState.ToString()
        + Environment.NewLine);
}

您的bgWorker_DoWork应该看起来像这样:

void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
    while (!bgw.CancellationPending)
    {
        System.Threading.Thread.Sleep(new TimeSpan(0, 0, 30));
        var status = ResultOfPing(e.Argument as Uri);
        bgw.ReportProgress(0, status);
    }

    e.Cancel = true;
}

,您应该这样称呼它:

bgWorker.RunWorkerAsync(url);

您还有第二个问题。 BackgroundWorker创建一个线程,您的线程将把大部分时间都花在定时器上或等待网络响应。那是线程的不良使用。您最好使用完成回调或async/await

答案 1 :(得分:1)

使用Microsoft的Reactive Framework(NuGet“ System.Reactive.Windows.Forms”并添加using System.Reactive.Linq;),然后您可以执行以下操作:

private void button1_Click(object sender, EventArgs e)
{
    var url = textBox1.Text;
    Observable
        .Interval(TimeSpan.FromMinutes(0.5))
        .SelectMany(_ => Observable.Start(() => Status(url)))
        .ObserveOn(this)
        .Subscribe(status => textBox2.AppendText("Status of: " + status + "\n"));
}

然后,您只需更改Status即可获得以下签名:string Status(string url)

就是这样。没有背景工作者。没有调用。并且Status很好地在后台线程上运行。

答案 2 :(得分:0)

后台工作程序正在线程池线程上运行,但是对状态和睡眠的调用正在UI线程上运行。您需要将这些内容移回bgWorker_DoWork。

答案 3 :(得分:0)

尝试以下代码:

public partial class Form1 : Form
{
    bool cancel;

    public Form1()
    {
        InitializeComponent();

    }

    public void StartPinging()
    {
        this.cancel = false;
        startButton.Enabled = false;
        stopButton.Enabled = true;
        responseBox.Clear();
        responseBox.AppendText("Starting to ping server.");
        responseBox.AppendText(Environment.NewLine);
        var bw = new BackgroundWorker
        {
            WorkerReportsProgress = false,
            WorkerSupportsCancellation = true
        };
        bw.DoWork += (obj, ev) =>
        {
            while (!cancel)
            {
                // Ping Server Here
                string response = Server.PingServer();

                this.Invoke(new UiMethod(() =>
                {
                    responseBox.AppendText(response);
                    responseBox.AppendText(Environment.NewLine);
                }));
            }
        };

        bw.RunWorkerCompleted += (obj, ev) =>
        {
            this.Invoke(new UiMethod(() =>
            {
                responseBox.AppendText("Stopped pinging the server.");
                responseBox.AppendText(Environment.NewLine);
                startButton.Enabled = true;
                stopButton.Enabled = false;
            }));
        };

        bw.RunWorkerAsync();
    }

    delegate void UiMethod();


    private void startButton_Click(object sender, EventArgs e)
    {
        StartPinging();
    }

    private void stopButton_Click(object sender, EventArgs e)
    {
        responseBox.AppendText("Cancelation Pressed.");
        responseBox.AppendText(Environment.NewLine);
        cancel = true;
    }
}

public class Server
{
    static Random rng = new Random();
    public static string PingServer()
    {
        int time = 1200 + rng.Next(2400);
        Thread.Sleep(time);
        return $"{time} ms";
    }
}

scr

答案 4 :(得分:-1)

Erwin,在处理C#-线程和UI元素时,通常会遇到跨线程操作,即带有UI线程的后台线程。这种交互需要在Invoke的帮助下以线程安全的方式进行,以避免无效的操作。

请查看以下资源:InvokeRequired部分。 https://docs.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls