DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: jeff1314
今日帖子: 10
在线用户: 11
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
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说明。
----------------------------------------------
-
作者:
男 hardnut (麦轲数据管家) ★☆☆☆☆ -
普通会员
2021/8/19 16:49:38
3楼: 楼主的问题应该是这个API放在子线程中执行时失败, 但在主线程中可以. 并没有在并发运行
----------------------------------------------
UniKeeper V10.40 -- 您最贴心的个人数据管理助手
作者:
男 sczhyq (旺财) ★☆☆☆☆ -
普通会员
2021/8/19 16:57:17
4楼: 执行前先加锁,执行后再解锁, 主线程与子线程就不会冲突
----------------------------------------------
我84砖家
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
2021/8/19 18:13:26
5楼: 和供应商聊了,他们的dll就是通过createfile的方式实现串口读写
现在的问题是,放在非主线程里面也是单线程读写串口,也做了CriticalSection保护(实际用不上),但是对串口设备的读写就是和放在主线程中的不一样,正常情况下这个API通过串口和外部设备有上百次的读写交互,所以耗时比较长,但是放在非主线程立即就返回了,就好像是同步操作变成了异步一样

之所以放在非主线程中,就是为了防止这段操作卡死主界面
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2021/8/19 18:37:19
6楼: 一般来说io操作总是串行的,对于子线程来说,并不会有太大改变,也就是说放在子线程里和都在主线程里执行的区别只是顺序不同,加不加临界区都没什么区别。

至于执行失败的问题,兰州可以在执行子线程里引入消息泵然后再看看是否可以执行成功。
----------------------------------------------
--
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
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内部的消息机制造成了影响,比如子线程的上下位屏蔽或者复位了某些消息传递?
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
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(),那就没办法了。

如果是每操作一次串口:打开,关闭。那可以在主线程里关闭串口,再打开,再关闭。。。
----------------------------------------------
-
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
2021/8/19 22:29:09
10楼: 这个问题我觉得和多线程访问串口资源没什么关系,就是一个dll导出的函数,实现了通过串口读写一个外部设备,内部封装了一些业务逻辑,放在主线程运行,和另起一个线程来运行,实际的效果不同,最大可能怀疑是主线程和子线程的上下文环境不同。
dll_func1 打开串口
dll_func2读写串口
dll_fun3关闭串口
现在只是把dll_func2放在主线程运行和放在子线程运行的区别,其他的操作都不变
----------------------------------------------
-
作者:
男 hardnut (麦轲数据管家) ★☆☆☆☆ -
普通会员
2021/8/20 8:15:40
11楼: 先用个最简单的程序, 用最传统的TThread试试,
----------------------------------------------
UniKeeper V10.40 -- 您最贴心的个人数据管理助手
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/8/20 11:52:30
12楼: dll_func1 打开串口
dll_func2读写串口
dll_fun3关闭串口

你把这三步,放到一个线程里面去做试试。如果主线程做第一步,其它线程做第二步,说不定有问题。
----------------------------------------------
-
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
2021/8/20 19:01:16
13楼: 找到问题点了
这个dll需要传入一个PAnsiChar类型的串口名字,之前的代码中用一个函数拼接“COM”和串口号返回一个PAnsiChar的Result传给dll,改成在调用dll之前原地拼接出“COMx”的String并转成PAnsiChar给dll就没有问题

跟踪了一下代码,在子进程上下文中,原来传递给dll的PAnsiChar指向的内容变得不确定,具体原因后面再继续琢磨

再次感谢楼上各位的指教
----------------------------------------------
-
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2021/8/20 23:02:58
14楼: 如果函数直接返回指针类型那么就要自己手动去管理内存,也就是说你要显式的申请跟回收内存。
比如如下处理方式:
function newstr(const s: ansistring): pansichar;
begin
  getmem(result, length(s) + 1);
  strpcopy(result, pointer(s));
end;
----------------------------------------------
--
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
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;

其实我真没看出来上面两句有什么区别
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2021/8/21 10:12:28
16楼: 为了验证你的问题,你可以把这段代码放在线程里面,然后把那个函数再包进同步方法里面,也就是实际上还是在主线程里面执行。然后看看效果。
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行66.40625毫秒 RSS