读书人

(急)完事一个线程再重新new初始化该

发布时间: 2013-07-08 14:13:00 作者: rapoo

(急)结束一个线程再重新new初始化该线程,新线程跑起来很慢,阻滞(线程里有Lock块)?
这是一个和硬件打交道的线程,通过USB转CAN总线连接电机,通过调用电机厂商提供的API控制电机,因为还需要实时得到电机的信息,所以还开了个线程,为了防止同时访问总线,造成总线冲突,所以加了Lock同步块,具体是通过操作杆去控制,连接操作杆的是一个USB转232的串口,因为串口会重新插拔或松脱之类,所以做了个按钮,相当于重新初始化:过程就是1.彻底停止操作杆控制线程,2.关闭操作杆串口,3.关闭电机连接,4.重新打开操作杆串口,5.重新打开电机连接,6.初始化操作杆控制线程。
彻底停止操作杆线程的语句:


if (threadJoyStickMaxon != null && threadJoyStickMaxon.IsAlive)
{
joyStickLeftPortKeepReading = false;
threadJoyStickMaxon.Join();
threadJoyStickMaxon = null;
}

线程变量定义的是成员变量。
关键就在于这句 threadJoyStickMaxon = null;
如果不写这句,再初始化新的线程之前,线程的IsAlive属性为false,ThreadState为Stopped,同时界面就会很卡。如果写这句,再初始化新的线程之前,线程的IsAlive属性却为true,ThreadState却为Start,同时界面就不卡了。使线程赋为null,加速了GC对其的释放,但是在没有初始化前为什么它的IsAlive属性却为true,ThreadState却为Start呢。
更关键的使用后一种,会出现调用电机的API出现严重的错误:“不能向非托管代码传递无效的函数指针之类的。”原先初始化电机连接放在一个线程中,关闭电机连接直接放在UI线程中就会出现VC++ RuntimeLibrary Error之类的内存错误,所以后来处理的方式是在初始化线程中连接完电机后就让它WaitOne,最后关闭软件的时候通过手工重置事件再唤醒它,执行后续的关闭电机连接的操作,这样就不会在关闭软件时报这样的错误。
初始化电机线程:

void CreateManageThreadDeviceInit()
{
threadDeviceInit = new Thread(ManageThreadDeviceInit);
threadDeviceInit.Start();
}
void ManageThreadDeviceInit()
{
OpenJoystickerLeftport();
InitMaxon();
joyStickLeftPortKeepReading = true;
CreateManageThreadJoystickMaxon();

CreateManageThreadReadMaxonStatus(); //打开读电机状态线程

deviceInitThreadResetEvent.WaitOne(); //**MJ:让初始化硬件线程阻塞,直到关闭软件的时候在唤醒它,关闭Maxon电机连接

try //关闭电机连接,电机API提供的方式
{
maxonErrorPromptMessage = "CloseDevice"; //**MJ:如果没有成功打开Maxon电机,关闭Maxon电机连接时或者不关,都会有VC++ RunTime Library Error,定时器初始化设备有时也会,线程初始化设备是每次都有
if (EposCmd.Net.VcsWrapper.Device.VcsCloseDevice(keyHandle, ref errorCode) == 0)


{
ShowErrorInformation(errorCode, maxonErrorPromptMessage);
}
EposCmd.Net.VcsWrapper.Device.Cleanup();
}
catch (Exception ex)
{
writeExceptionToLogFile("MaxonCloseDevice", ex.Message);
}
}

private int InitMaxon() //初始化电机,电机API提供的方式
{
EposCmd.Net.VcsWrapper.Device.Init();
string deviceName = "EPOS2";
string protocolStackName = "CANopen";
int startOfSelection = 1; //1:Get first selection string,0:Get next selection string
string interfaceNameSel="";
int endOfSelection=0;
if(EposCmd.Net.VcsWrapper.Device.VcsGetInterfaceNameSelection(deviceName, protocolStackName, startOfSelection, ref interfaceNameSel, ref endOfSelection, ref errorCode)==0)
{
maxonErrorPromptMessage = "InitMaxon";
ShowErrorInformation(errorCode, maxonErrorPromptMessage);
return 0;
}


keyHandle = EposCmd.Net.VcsWrapper.Device.VcsOpenDevice("EPOS2", "CANopen", interfaceNameSel, "CAN0", ref errorCode);
if (keyHandle == 0)
{
maxonErrorPromptMessage = "InitMaxon";
ShowErrorInformation(errorCode, maxonErrorPromptMessage);
return 0;
}
if (EposCmd.Net.VcsWrapper.Device.VcsSetProtocolStackSettings(keyHandle, maxonBaudrate, maxonTimeout, ref errorCode) == 0) //1 if successful, 0 otherwise
{
maxonErrorPromptMessage = "InitMaxon";
ShowErrorInformation(errorCode, maxonErrorPromptMessage);
return 0;
}
return 1;
}


关闭硬件线程:

void CreateManageThreadCloseDevice()
{
threadCloseDevice = new Thread(new ThreadStart(ManageThreadCloseDevice));
//threadCloseDevice.IsBackground = true;
threadCloseDevice.Start();
}

void ManageThreadCloseDevice()
{
CloseAllDevice();
}

void CloseAllDevice()


{
int i = 0;
#region 关闭左操作杆线程和左操作杆串口
if (threadJoyStickMaxon != null && threadJoyStickMaxon.IsAlive)
{
joyStickLeftPortKeepReading = false; //左操作杆控制线程
threadJoyStickMaxon.Join();
threadJoyStickMaxon = null;
}
CloseJoystickerLeftport();
#endregion

if (threadReadMaxonStatus != null && threadReadMaxonStatus.IsAlive) //关闭读电机线程
{
readMaxonStatusFlag = false;
threadReadMaxonStatus.Join(); //**MJ:因为点击关闭连接按钮,GetPulsePositionIs会有错误(线程还没有完全退出时,已关闭电机连接)
threadReadMaxonStatus = null;
}

deviceInitThreadResetEvent.Set();

}


以上代码是从工程里抽出来的,操作杆里控制电机的函数和读电机状态里的函数加了lock,据我所知,lock和Muntex不一样,它不需要释放,右括号的结束就标明了临界区的结束,所以不需要对其解锁。
------解决方案--------------------


不是你的分数少,而是这种线程锁之间的问题千奇百怪,仅只简单看你代码很难找到问题,这个主要在于自己设计线程锁时就要对线程之间的关系和独占资源理顺。如果对线程锁不太理解的话,简单的用法可以直接用lock块把需要共享的独占资源锁起来,这个就不需要太精通和理解线程访问冲突和死锁问题。
[解决办法]
大概看了一下,没理的很顺。你线程中开线程,嵌套太多了,不是多开一个线程就会更快的,单核cpu会吧多线程顺序执行,而且你事件的同步,虽然无法确定你的问题,但可以给你几个建议:
1.threadJoyStickMaxon = null; 这样并不能加速垃圾回收,强制垃圾回收是直接调用 Collect 方法,例如:
int layer = GC.GetGeneration(threadJoyStickMaxon);
GC.Collect(layer);
GC.WaitForFullGCComplete();
但这样做,如果在重复率很高的地方写,回严重影响性能。但这样才是比较全面的释放一个对象。
2.你线程嵌套打开的过多,这个时序结构是否可以简化优化一下再来看,你的问题或许就在简化的流程中不存在了。

读书人网 >C#

热点推荐