当我们尝试从不同的线程更新UI时,会抛出一个异常The calling thread cannot access this object because a different thread owns it
。好的,对我来说没问题。
但为什么下面提到的代码没有抛出类似的异常呢?线程t1和t2可能同时写入value
。
public class myclass
{
string value;
public myclass()
{
Thread t1 = new Thread(method1);
t1.Start();
Thread t2 = new Thread(method2);
t2.Start();
}
public void method1()
{
while (true)
{
value = "method1";
}
}
public void method2()
{
while (true)
{
value = "method2";
}
}
}
答案 0 :(得分:2)
UI控件具有线程关联性。这意味着应该在创建它们的同一个线程上访问它们。这是因为WinForms/WPF
实际上只是Win32
功能的包装器,管理Win32
中的窗口和子控件的代码不是线程安全的。因此,您的表单和子控件只能在创建它们的同一个线程上访问。创建它们的那个,即进程Main Thread
。
相反,可以在任何线程上创建,访问和修改类变量(假设它们暴露给调用者)。这意味着除非你制作它们,否则它们不是Thread Safe
。
答案 1 :(得分:0)
string是引用类,因此它的赋值是原子的。 ui控件更复杂,它最终与消息循环和线程相关联。这两种情况之间没有简单的转换。大多数变量可以被多个线程访问,它们的行为可能是未定义的或不正确的。 UI对象验证调用者是否在正确的线程上或抛出异常。
此代码是竞争条件的一个小例子。两个线程将尝试递增然后递减计数器相同的次数。有时你不会得到正确的输出。
public class myclass
{
int value = 0;
const int tries = 10000;
public int Go()
{
Thread t1 = new Thread(method1);
t1.Start();
Thread t2 = new Thread(method2);
t2.Start();
t1.Join();
t2.Join();
return value;
}
public void method1()
{
for (int x = 0; x < tries; x++)
{
value++;
}
}
public void method2()
{
for (int x = 0; x < tries; x++)
{
value--;
}
}
}
class Program
{
static void Main(string[] args)
{
var c = new myclass();
int counter = 0;
for(int x = 0 ; x < 100 ; x++)
{
if(c.Go() != 0)
{
Console.WriteLine("Iteration {0} doesn't = 0", x);
}
}
}