我复制了以下示例Microsoft Thread Example
下面给出了代码 但我在“this.progressBar1.Value = newval;”一行上收到错误声明“跨线程操作无效:控制'progressBar1'从其创建的线程以外的线程访问。”
可能是什么问题? 谢谢 DAMO
C#代码
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("This is the main thread");
}
}
答案 0 :(得分:2)
首先,我强烈建议使用一些更高级别的技术,例如Tasks,而不是直接使用Thread
类。任务类不仅更容易使用,更有效,更容易编写,更容易避免您最近遇到的问题。
您尝试从非UI线程更新UI对象的代码的主要问题。 UI技术(如Windows窗体或WPF)要求只有创建UI对象的线程才能访问其属性。
要解决此问题,您应该将控制从非UI线程封送到UI线程。并且有很多选项可以做到这一点(但是所有这些选项只是一个名为SynchronizationContext概念的语法糖):
// storing SynchronizationContext in the private field of your form
private SynchronizationContext _syncContext = SyncrhonizationContext.Current;
private void MethodFromTheSeparateThread()
{
// Marshaling control to UI thread
_syncContext.Post(d =>
{
// Put all your code that access UI elements here
}, null);
}
使用InvokeRequired
/ Invoke
正如Gregor所提到的那样
使用TaskScheduler.FromSynchronizationContext
private void ImplementLongRunningOperation()
{
int id;
string name;
Task.Factory.StartNew(() =>
{
// our long-runing part. Getting id and name
id = 42;
name = "Jonh Doe";
}).ContinueWith(t =>
{
// Handling results from the previous task.
// This callback would be called in UI thread!
label1.Text = id.ToString();
label2.Text = name;
}, TaskScheduler.FromSynchronizationContext);
}
正如我所提到的,如果您正在使用.NET 4.0+,那么最后一种方法(使用Tasks
)是一种更好的方法。这不仅可以使您从一些低级别类中省去,而且还可以实现更清晰的设计,因为您可以清楚地分离单独的步骤,如获取数据和处理它们。
答案 1 :(得分:1)
您必须调用新的委托:
delegate void ThreadTaskDelegate();
private void ThreadTask()
{
if (this.InvokeRequired)
{
ThreadTaskDelegate del = new ThreadTaskDelegate(ThreadTask);
this.Invoke(del, null);
}
else
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
}
快乐的编码! :)
答案 2 :(得分:1)
这个例子很糟糕。您必须访问创建它们的线程中的控件。这几乎总是主要的UI线程。 (可以为不同的表单创建单独的UI线程,每个表单都有自己的消息泵。但是现在不要担心。)
后台线程在访问Controls之前必须使用Control.Invoke(Delegate)更改为主UI线程。然后,当UI工作完成后,尽快离开UI线程。
例如:
private void ThreadTask()
{
// This code runs in the background thread.
while (true)
{
if (this.InvokeRequired)
{
// In order to access the UI controls, we must Invoke back to the UI thread
this.Invoke(new ThreadStart(SetRandomProgress));
}
else
{
// We are already in the UI thread, so we don't have to Invoke
SetRandomProgress();
}
// Wait briefly. This wait happens in the background thread.
// During this time, the UI is still responsive, because it is not blocked.
// You can verify this by tweaking the duration to something longer (say, 5000 ms).
Thread.Sleep(100);
}
}
private void SetRandomProgress()
{
Random rnd = new Random();
int stp = this.progressBar1.Step * rnd.Next(-1, 2);
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
答案 3 :(得分:1)
您可以像这样重写代码,您的progressBar
将在UI线程中更新,调用通过委托访问progressBar
的方法。检查代码:
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
Random rnd = new Random();
while (true)
{
int randValue = rnd.Next(-1, 2);
progressBar1.Invoke(new updater(UpdateProgressBar), new object[] {randValue});
Thread.Sleep(100);
}
}
private delegate void updater(int value);
private void UpdateProgressBar(int randValue)
{
int stp = this.progressBar1.Step * randValue;
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}