|
|
导航: |
论坛 -> DELPHI技术
斑竹:liumazi,sephil |
|
作者: |
|
2021/8/19 14:41:32 |
标题: |
主线程和非主线程运行同一段的代码的问题 |
浏览:1437 |
|
加入我的收藏 |
楼主: |
设备供应商提供了一个dll,是设备串口读写相关的 其中一个API函数放在主线程中运行,大约需要10秒才正常返回 如果放在QWorker Post出的非主线程中运行,立即就返回了(设备操作实际上是失败的)
是不是主线程和非主线程的上下文环境会影响到串口读写的执行?
请大神解惑,多谢
----------------------------------------------
- |
作者: |
janker (janker) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2021/8/19 14:54:44 |
1楼: |
肯定,串口是独占的
----------------------------------------------
-
|
作者: |
janker (janker) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2021/8/19 15:11:27 |
2楼: |
通俗讲,一个线程打开了一个串口,只有关闭后,另一个线程才能使用这个串口。 如果要深入点,看看winAPI的CreateFile()的OVERLAPPED 与 FILE_FLAG_OVERLAPPED说明。
----------------------------------------------
-
|
作者: |
|
2021/8/19 16:57:17 |
4楼: |
执行前先加锁,执行后再解锁, 主线程与子线程就不会冲突
----------------------------------------------
我84砖家
|
作者: |
|
2021/8/19 18:13:26 |
5楼: |
和供应商聊了,他们的dll就是通过createfile的方式实现串口读写 现在的问题是,放在非主线程里面也是单线程读写串口,也做了CriticalSection保护(实际用不上),但是对串口设备的读写就是和放在主线程中的不一样,正常情况下这个API通过串口和外部设备有上百次的读写交互,所以耗时比较长,但是放在非主线程立即就返回了,就好像是同步操作变成了异步一样
之所以放在非主线程中,就是为了防止这段操作卡死主界面
----------------------------------------------
-
|
作者: |
|
2021/8/19 18:37:19 |
6楼: |
一般来说io操作总是串行的,对于子线程来说,并不会有太大改变,也就是说放在子线程里和都在主线程里执行的区别只是顺序不同,加不加临界区都没什么区别。
至于执行失败的问题,兰州可以在执行子线程里引入消息泵然后再看看是否可以执行成功。
----------------------------------------------
--
|
作者: |
|
2021/8/19 19:15:40 |
7楼: |
感谢楼上的回复,消息泵我的琢磨琢磨 代码大致是这样的,用到了QDAC.QWorker框架 Wokers.Post( procedure(AJob:PQJob) begin DLL_Procesure; end, nil,False); 如果把最后的false改成true放在主线程中执行,dll_procedure占用10秒左右结果就是正确的,如果是false放在独立的子线程中执行,dll_procedure立即就返回
不知道是不是主线程和子线程的尚未问对dll_procedure内部的消息机制造成了影响,比如子线程的上下位屏蔽或者复位了某些消息传递?
----------------------------------------------
-
|
作者: |
|
2021/8/19 20:01:52 |
8楼: |
1. 对串口操作,你可以理解为对系统资源的操作,其实和操作内存一样:不要同时多个线程操作同一个资源。如果多个线程要同时操作同一个资源,加上临界区等等,让它不能同时并发;
2. 具体到楼主的问题,因为你不是自己在操作串口,而是在调用 DLL,那这个 DLL 的调用应该有些什么规则,需要 DLL 的开发者来说。我们在这里瞎猜没有意义。
比如,这个 DLL 是一个 COM,那你用线程去调用它,就应该加上 CoInitial 类似的东西,否则可能会失败。我这里只是打个比方。谁知道 DLL 的开发者是怎么做的呢?得问开发者才行。
这个 DLL,比如假设我来开发,我可能采用这样一种架构: 1. DLL 对串口(或者其它什么鬼资源的读写),我自己有个线程去做,不让外面的用户调用来直接做。因为我不知道用户会用什么线程或者什么方式来调用。 2. 我给用户的调用接口,其实仅仅是让用户把数据丢入我开的缓冲区。缓冲区里面有了数据,我自己的线程去发送数据。 这样的架构,把用户和我的 DLL 内部的实际代码隔离了,避免出问题。
当然,这样开发,也仅仅是一种思考。还有很多其它办法,看实际应用场景来决定。
----------------------------------------------
-
|
作者: |
janker (janker) |
★☆☆☆☆ |
-
|
盒子活跃会员 |
|
2021/8/19 22:04:44 |
9楼: |
串口操作肯定和什么临界区保护没关系的,线程中打开了(对应CreateFile),如果没有CloseHandle(),即使串口不工作,也是占着,其它线程也不能用。
DLL估计是一直占着串口了,没有closeHandle().
这样的话,有点麻烦啊。。。。
如果DLL的串口操作函数,是一开始就打开串口,然后就没有CloseHandle(),直到线程结束才CloseHandle(),那就没办法了。
如果是每操作一次串口:打开,关闭。那可以在主线程里关闭串口,再打开,再关闭。。。
----------------------------------------------
-
|
作者: |
|
2021/8/19 22:29:09 |
10楼: |
这个问题我觉得和多线程访问串口资源没什么关系,就是一个dll导出的函数,实现了通过串口读写一个外部设备,内部封装了一些业务逻辑,放在主线程运行,和另起一个线程来运行,实际的效果不同,最大可能怀疑是主线程和子线程的上下文环境不同。 dll_func1 打开串口 dll_func2读写串口 dll_fun3关闭串口 现在只是把dll_func2放在主线程运行和放在子线程运行的区别,其他的操作都不变
----------------------------------------------
-
|
作者: |
|
2021/8/20 11:52:30 |
12楼: |
dll_func1 打开串口 dll_func2读写串口 dll_fun3关闭串口
你把这三步,放到一个线程里面去做试试。如果主线程做第一步,其它线程做第二步,说不定有问题。
----------------------------------------------
-
|
作者: |
|
2021/8/20 19:01:16 |
13楼: |
找到问题点了 这个dll需要传入一个PAnsiChar类型的串口名字,之前的代码中用一个函数拼接“COM”和串口号返回一个PAnsiChar的Result传给dll,改成在调用dll之前原地拼接出“COMx”的String并转成PAnsiChar给dll就没有问题
跟踪了一下代码,在子进程上下文中,原来传递给dll的PAnsiChar指向的内容变得不确定,具体原因后面再继续琢磨
再次感谢楼上各位的指教
----------------------------------------------
-
|
作者: |
|
2021/8/20 23:02:58 |
14楼: |
如果函数直接返回指针类型那么就要自己手动去管理内存,也就是说你要显式的申请跟回收内存。 比如如下处理方式: function newstr(const s: ansistring): pansichar; begin getmem(result, length(s) + 1); strpcopy(result, pointer(s)); end;
----------------------------------------------
--
|
作者: |
|
2021/8/21 8:55:13 |
15楼: |
函数返回的指针实际上指向一个自定义类的私有成员, type myClass=class private FPortName:string; function GetPortName:PAnsiChar; ... public procedure AccessComPort; ... end;
.....
function myClass.GetPortName:PAnsiChar; begin Result:=PAnsiChar((AnsiString(FPortName))); end;
procedure myClass.AccessComPort; begin // DLL_Func(GetPortName); //这个放在子线程中就会出问题 DLL_Func(PAnsiChar((AnsiString(FPortName)))); //改成这样就可以 end;
其实我真没看出来上面两句有什么区别
----------------------------------------------
-
|
作者: |
|
2021/8/21 10:12:28 |
16楼: |
为了验证你的问题,你可以把这段代码放在线程里面,然后把那个函数再包进同步方法里面,也就是实际上还是在主线程里面执行。然后看看效果。
----------------------------------------------
-
|
|