首先,我想说我是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一起工作过,并且您可以想象它令人困惑。我看了很多其他文章,但似乎无法理解。抱歉,如果代码看起来很疯狂,我正在尝试学习。
答案 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";
}
}
答案 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