MethodImplOptions.Synchronized在C#.Net中不工作
我使用多个线程向机器人发送串行端口数据 当我在MethodImplOptions.Synchronized在C#.Net中不工作,c#,.net,multithreading,C#,.net,Multithreading,我使用多个线程向机器人发送串行端口数据 当我在BotNavigation上调用导航方法时,它们彼此不同步 SerialPort port = new SerialPort("COM3"); BotNavigation bot = new BotNavigation(); // must execute three times, but it only executes once. bot.TakeForward(3, port); // must execute once, an
BotNavigation
上调用导航方法时,它们彼此不同步
SerialPort port = new SerialPort("COM3");
BotNavigation bot = new BotNavigation();
// must execute three times, but it only executes once.
bot.TakeForward(3, port);
// must execute once, and it works fine.
bot.TurnLeft(1, port);
你能帮我解决这个问题吗
这是代码
namespace NavigateBot
{
class BotNavigation
{
[MethodImpl(MethodImplOptions.Synchronized)]
public void TakeForward(int distanceTime, SerialPort port)
{
int count = 0;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
while (true)
{
port.WriteLine("F");
if (count >= distanceTime)
{
Thread.CurrentThread.Abort();
break;
}
Thread.Sleep(distanceTime * 250);
count++;
}
}));
thread.IsBackground = true;
thread.Start();
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void TakeBackward(int distanceTime, SerialPort port)
{
int count = 0;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
while (true)
{
port.WriteLine("B");
if (count >= distanceTime)
{
Thread.CurrentThread.Abort();
break;
}
Thread.Sleep(distanceTime * 250);
count++;
}
}));
thread.IsBackground = true;
thread.Start();
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void TurnLeft(int distanceTime, SerialPort port)
{
int count = 0;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
while (true)
{
port.WriteLine("L");
if (count >= distanceTime)
{
Thread.CurrentThread.Abort();
break;
}
Thread.Sleep(distanceTime * 250);
count++;
}
}));
thread.IsBackground = true;
thread.Start();
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void TurnRight(int distanceTime, SerialPort port)
{
int count = 0;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
while (true)
{
port.WriteLine("R");
if (count >= distanceTime)
{
Thread.CurrentThread.Abort();
break;
}
Thread.Sleep(distanceTime * 250);
count++;
}
}));
thread.IsBackground = true;
thread.Start();
}
}
}
您正在使用
[MethodImpl(methodimpoptions.Synchronized)]
同步这些方法,但这些方法会启动自己的线程将数据发送到串行端口
线程内发生的操作未同步,因为原始方法调用(启动线程)已返回并释放由[MethodImpl(methodimpoptions.synchronized)]
创建的锁
如果希望机器人的动作按顺序执行,可以在线程自己的私有锁对象上同步线程本身
进一步建议:
- 无需调用
,只需使用Thread.Abort()
作为count
循环条件while
- 您可以使用线程池来避免自己创建新线程的开销
namespace NavigateBot
{
class BotNavigation
{
private readonly object _serialLock = new object();
[MethodImpl(MethodImplOptions.Synchronized)]
public void TakeForward(int distanceTime, SerialPort port)
{
int count = 0;
ThreadPool.QueueUserWorkItem(
delegate
{
lock (_serialLock)
{
while (count < distanceTime)
{
port.WriteLine("F");
Thread.Sleep(distanceTime*250);
count++;
}
}
});
}
...
[MethodImpl(MethodImplOptions.Synchronized)]
public void TurnLeft(int distanceTime, SerialPort port)
{
int count = 0;
ThreadPool.QueueUserWorkItem(
delegate
{
lock (_serialLock)
{
while (count < distanceTime)
{
port.WriteLine("L");
Thread.Sleep(distanceTime*250);
count++;
}
}
});
}
...
}
}
namespace-NavigateBot
{
类导航
{
私有只读对象_serialLock=新对象();
[MethodImpl(MethodImplOptions.Synchronized)]
public void TakeForward(int distanceTime,SerialPort)
{
整数计数=0;
ThreadPool.QueueUserWorkItem(
代表
{
锁(_serialLock)
{
while(计数<距离时间)
{
书面港(“F”);
线程睡眠(距离时间*250);
计数++;
}
}
});
}
...
[MethodImpl(MethodImplOptions.Synchronized)]
公共无效左转(整数距离时间,串行端口)
{
整数计数=0;
ThreadPool.QueueUserWorkItem(
代表
{
锁(_serialLock)
{
while(计数<距离时间)
{
写线港(“L”);
线程睡眠(距离时间*250);
计数++;
}
}
});
}
...
}
}
您使用的Thr同步属性[MethodImpl(methodimpoptions.Synchronized)]
仅同步您的方法,而不同步在这些方法中启动的线程
当您调用botForward.TakeForward(3,端口)时
这会阻止任何pother线程在同一BotNavigation实例上同时调用此方法,但此方法运行速度非常快,甚至不能说此方法的内部线程在方法返回和释放锁之前调用Port.Writeline一次
此外,线程在当前状态下使用未受保护的上下文变量运行:SerialPort-port
所有线程都在此类的同一实例上工作,这是不安全的,只要SerialPort本身不是线程安全的
代码的目标还不清楚,但如果您将代码分成两个线程,您可能会节省:一个线程向bot发出命令,另一个线程保存通信类的实例,并以确定性方式处理命令(如线程安全队列)首先,您确实需要避免像瘟疫一样的
Thread.Abort()
。像这样重写一个方法是避免它的简单方法:
public void TakeBackward(int distanceTime, SerialPort port)
{
int count = 0;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
while (true)
{
port.WriteLine("B");
if (count >= distanceTime)
{
break;
}
else
{
Thread.Sleep(distanceTime * 250);
count++;
}
}
}));
thread.IsBackground = true;
thread.Start();
}
话虽如此,我将建议一种替代方法,使这段代码能够很好地为您工作
我建议您使用微软的反应式框架(NuGet“Rx Main”)
下面是代码的样子:
class BotNavigation
{
private EventLoopScheduler _eventLoop = new EventLoopScheduler();
private void Take(string message, int distanceTime, SerialPort port)
{
Observable
.Interval(TimeSpan.FromMilliseconds(250.0))
.Select(x => message)
.StartWith(message)
.Take(distanceTime)
.ObserveOn(_eventLoop)
.Subscribe(port.WriteLine);
}
public void TakeForward(int distanceTime, SerialPort port)
{
this.Take("F", distanceTime, port);
}
public void TakeBackward(int distanceTime, SerialPort port)
{
this.Take("B", distanceTime, port);
}
public void TurnLeft(int distanceTime, SerialPort port)
{
this.Take("L", distanceTime, port);
}
public void TurnRight(int distanceTime, SerialPort port)
{
this.Take("R", distanceTime, port);
}
}
EventLoopScheduler
基本上是一个后台线程,它将提供您需要的所有同步
Observable
// fire a timer every 250 ms
.Interval(TimeSpan.FromMilliseconds(250.0))
// change the timer output to the message
.Select(x => message)
// at the start instantly output a message
.StartWith(message)
// only take `distanceTime` messages
.Take(distanceTime)
// execute the output on the synchronised event loop thread
.ObserveOn(_eventLoop)
// write the value to the port
.Subscribe(port.WriteLine);
可观察对象创建您需要的时间和消息
Observable
// fire a timer every 250 ms
.Interval(TimeSpan.FromMilliseconds(250.0))
// change the timer output to the message
.Select(x => message)
// at the start instantly output a message
.StartWith(message)
// only take `distanceTime` messages
.Take(distanceTime)
// execute the output on the synchronised event loop thread
.ObserveOn(_eventLoop)
// write the value to the port
.Subscribe(port.WriteLine);
希望这很容易理解。不要调用
Thread.Abort()
-请注意,您的方法是同步的,但是它们会启动额外的线程,而这些线程在任何情况下都不会同步。从根本上说,不清楚您想要实现什么,但这看起来不是正确的方法…@Enigmativity在自己的线程中调用abort是安全的。但我大体上同意你的看法。在这种情况下,它是安全的。@SriramSakthivel-我甚至不认为它是安全的。唯一安全的时候是关闭程序时。@Enigmativity为什么?为什么你认为当一个线程自动中止时是不安全的?对不起。我使用的是Console.Out进行调试,而不是SerialPort。请尝试上面更新的代码。不客气。如果这个答案解决了你的问题,你可以通过接受答案并投票来感谢我,并给我打分。