无法从单独的线程修改变量
本文关键字:线程 修改 变量 单独 | 更新日期: 2023-09-27 17:57:20
所以我正在制作一个 C# 应用程序,它必须连续读取和显示文本文件的内容,同时允许用户在文本框中输入内容并将其附加到该文件的末尾。
我通过在单独的线程上运行我的 read
方法来做到这一点,但是更改存储显示文本文件内容的变量是导致问题的原因。最初,我尝试使用一种方法来执行此操作,但是这种方法不起作用,并且给出了"跨线程操作无效"错误。然后我尝试应用我在 MSDN 上找到的一些代码,但现在在线程结束后更新变量!
请帮忙。
partial class MainForm
{
delegate void SetTextCallback(string text);
public static string msg;
public static string name;
public void InitClient()
{
name = "public.txt";
Console.WriteLine(name);
if(!File.Exists(name))
{
File.Create(name);
File.AppendAllText(name, "Welcome to " + name);
}
Thread Read = new Thread(new ThreadStart(this.Client));
Read.Start();
while(!Read.IsAlive);
}
public void WriteText()
{
File.AppendAllText(name, this.InputBox.Text);
this.InputBox.Clear();
}
private void SetText(string text)
{
if (this.OutPut.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.OutPut.Text = text;
}
}
public void Client()
{
msg = File.ReadAllText(name);
Console.WriteLine(msg);
Thread.Sleep(300);
this.SetText(msg);
}
}
为什么线程的行为是这样的。如何修改我的代码,以便输出框的内容始终等于文本文件的内容。
欢迎任何建议。
你在这里遇到了多个问题,
- 文件的使用可能不是线程安全的。
- 您的方法不重复
- 您正在线程上
Sleep()
您可以通过放弃线程并使用简单的计时器来解决所有这些问题。
尝试使用后台工作线程,而不是创建新线程。后台工作线程将在单独的线程中运行其内容,并允许您在其工作时报告"进度"。此进度报告将始终在 UI 线程(或启动后台辅助角色的线程)上运行。
它还具有一个事件,该事件在后台工作线程完成时调用。这也在 UI 线程上运行。
此示例应帮助您入门。
更新:根据建议添加了一些非常基本的错误处理
这个想法是使用ReportProgress的用户数据(第二个参数)在需要时对UI线程进行更新。在本例中,它是一个字符串,但可以是任何对象。
此外,您可以使用DoWorkEventArgs的结果从后台工作生成最终结果。在这种情况下,我返回任何抛出的异常,否则为 null,但您也可以在此处返回您想要的任何内容。
正如Henk在他的评论中提到的,处理DoWork回调中发生的错误非常重要,因为这里发生的异常等将被吞噬,工作人员将完成,就好像没有发生任何不好的事情一样。
private BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
// This is the background thread
_backgroundWorker.DoWork += BackgroundWorkerOnDoWork;
// Called when you report progress
_backgroundWorker.ProgressChanged += BackgroundWorkerOnProgressChanged;
// Called when the worker is done
_backgroundWorker.RunWorkerCompleted += BackgroundWorkerOnRunWorkerCompleted;
}
private void BackgroundWorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
if (runWorkerCompletedEventArgs.Result != null)
{
// Handle error or throw it
throw runWorkerCompletedEventArgs.Result as Exception;
}
textBox1.Text = "Worker completed";
}
private void BackgroundWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
textBox1.Text = progressChangedEventArgs.UserState as string;
}
private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
try
{
for (int i = 0; i < 100 && !_backgroundWorker.CancellationPending; i++)
{
_backgroundWorker.ReportProgress(0, i + " cycles");
Thread.Sleep(100);
}
}
catch (Exception ex)
{
doWorkEventArgs.Result = ex;
}
}
private void startButton_Click(object sender, EventArgs e)
{
if (!_backgroundWorker.IsBusy)
_backgroundWorker.RunWorkerAsync();
}
private void cancelButton_Click(object sender, EventArgs e)
{
if(_backgroundWorker.IsBusy)
_backgroundWorker.CancelAsync();
}