以下代码在没有多线程的情况下工作正常。但如果我使用线程,它就会失败。如果我在checkedListBox中选择了多个项目,则第一个将被忽略而其他项目是随机的......
我认为提交数据存在问题。你觉得怎么样?
private void sendCom(String com)
{
//send command to selected item
int i=0;
String IP;
foreach (var item in checkedListBox1.CheckedItems)
{
Console.WriteLine(item.ToString());
IP = item.ToString();
theThreads[i] = new Thread(new ThreadStart(() => sendComThread(IP, com) ));
theThreads[i].Start();
//sendCom(IP, com);
i++;
}
}
private void sendComThread(String IP, String com)
{
// send an command
System.Console.WriteLine(IP + com);
}
答案 0 :(得分:4)
基本问题是您的变量捕获捕获单个变量,然后在所有线程之间共享。因此,每次线程读取共享变量时,它都会获得最近放在那里的任何值。除了语义错误之外,共享变量还有明确的数据竞争。
最简单的解决方案是为每个线程创建一个变量。只需在循环中移动变量的声明即可。像这样:
foreach (var item in checkedListBox1.CheckedItems)
{
....
String IP = item.ToString(); //NB variable declared inside loop
theThreads[i] = new Thread(new ThreadStart(() => sendComThread(IP, com) ));
....
}
现在每个线程都有自己的字符串变量的私有实例。
答案 1 :(得分:2)
这里的问题是你的线程正在新线程中从lambda表达式读取循环的状态,而不是将实际值传递给线程。
这意味着在新线程在CPU上进行调度时,循环实际上已经迭代到未知状态。这就是您的值显示为随机的原因。
以下是一步一步发生的事情:
() => sendComThread(IP, com)
lambda,引用这两个参数。theThreads[i].Start();
被调用,但不保证该线程中的代码将立即运行。在系统上的线程调度程序将上下文切换到不同的线程之前,当前代码可能会持续一段时间。IP = item.ToString();
,更改IP
的值。这可能不止一次发生。IP
的引用。IP
的状态未定义。解决方案是在创建线程期间传递值,以便将它们本地复制到线程:
struct SendComThreadParams
{
public string IP;
public string Com;
public SendComThreadParams(string ip, string com)
{
this.IP = ip;
this.Com = com;
}
}
private void sendCom(String com)
{
//send command to selected item
int i=0;
String IP;
foreach (var item in checkedListBox1.CheckedItems)
{
Console.WriteLine(item.ToString());
IP = item.ToString();
theThreads[i] = new Thread(new ParameterizedThreadStart(sendComThread));
theThreads[i].Start(new SendComThreadParams(IP, com));
i++;
}
}
private void sendComThread(object threadParam)
{
var p = (SendComThreadParams)threadParam;
// send an command
System.Console.WriteLine(p.IP + p.Com);
}
这会将参数正确复制到线程,以确保它们的值处于已定义的状态。
答案 2 :(得分:1)
theThreads[i].Start()
将不会立即运行新线程,并且IP变量可能会同时更改。
在for循环中定义IP变量将解决问题:
string IP = item.ToString();
答案 3 :(得分:0)
这是Polynomials代码的另一个版本。这次使用threadpools
struct SendComThreadParams
{
public string IP;
public string Com;
public SendComThreadParams(string ip, string com)
{
this.IP = ip;
this.Com = com;
}
}
private void sendCom(String com)
{
//send command to selected item
int i=0;
String IP;
foreach (var item in checkedListBox1.CheckedItems)
{
Console.WriteLine(item.ToString());
IP = item.ToString();
ThreadPool.QueueUserWorkItem(new WaitCallback(sendComThread), (object)new SendComThreadParams(IP, com));
i++;
}
}
private void sendComThread(object threadParam)
{
var p = (SendComThreadParams)threadParam;
// send an command
System.Console.WriteLine(p.IP + p.Com);
}