问一个线程模型的问题

onlycs 2011-12-02 09:45:18
标题党,哈哈,帮我看看下面模型怎么优化下吧,总感觉比较乱。
说明:数据库连接因为其他问题,只能放在线程函数中。

UINT ReadResource(LPVOID pParam)
{
InterlockedIncrement(&g_nReadThreadNum);
sReadThreadInit* pRdInit = (sReadThreadInit*)pParam;
CANNaviReadResource cReadResource;
初始化成员变量
BOOL bRet = cReadResource.OpenConnect(); //连接数据库
从全局队列中取数据(两个队列A,B,A中没数据才从B中取
while(bRet)
{
如果两个队列都没数据,线程挂起,并把线程状态设置未挂起

判断退出标志
if(A队列有数据)
{
执行对应函数

if(g_bEndThreadAll || g_nWriteThreadNum == 0) //判断用户发出退出请求或者写线程退出
{
if(当前只要自己处于工作状态)
{
将所有读线程的状态设置为退出;
激活所有线程;
}
break;
}
}
else
{

if(当前只要自己处于工作状态)
{
将所有读线程的状态设置为退出;
激活所有线程;
}
break;

}
从全局队列中取数据(两个队列A,B,A中没数据才从B中取

}
delete pRdInit;
InterlockedDecrement(&g_nReadThreadNum);
return 0;
}
...全文
151 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
onlycs 2011-12-13
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 dfasri 的回复:]

引用 20 楼 onlycs 的回复:
对于读写线程定义,我是以数据库来区分的。从数据库中读取数据,称之为读线程。反之,为写线程。

只要你简单转变一下思维, 把线程的读还是写跟业务区分开来, 就可以很清楚你的业务数据流向了.

用上线程安全的FIFO队列(用无锁效率高点, 临界区效率低点, 但不影响数据流动), 这样就可以直接按照你的描述来做事了.
首先建立N个"读线程", 并行……
[/Quote]
非常感谢您的耐心指导。我现在明白多了。我之前对队列AB的访问是交叉访问的。所以有些乱。如你所说,完全可以将对A,B访问,流水话。
dfasri 2011-12-13
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 onlycs 的回复:]
对于读写线程定义,我是以数据库来区分的。从数据库中读取数据,称之为读线程。反之,为写线程。
[/Quote]
只要你简单转变一下思维, 把线程的读还是写跟业务区分开来, 就可以很清楚你的业务数据流向了.

用上线程安全的FIFO队列(用无锁效率高点, 临界区效率低点, 但不影响数据流动), 这样就可以直接按照你的描述来做事了.
首先建立N个"读线程", 并行读取A, 假如A有数据, 那么处理完成后放入"共享区", 直到A读取完成为止. 然后转入B列表的读取, 假如B有数据, 则查询数据库, 生成N条数据写入A列表. 这个过程被N个"读线程"并行进行, 最终输出到"共享区", 由"写线程"读取"共享区", 写入另外一个数据库. 也就是这样一个流程.

不过我建议你还是简化一下, 建立一个线程是专门读取列表B数据的, 建立一个线程专门读取A数据的.
假如外部要写入一个需要查询数据库的, 那么写进B列表, 假如外部写入一个不需要查询数据库的, 写入A列表, 同时, B列表的读取线程, 也会在生成数据后向A列表写入数据, 这样就可以建立一个流水线的模式.

但这种情况下, 很明显B列表的读取会成为瓶颈. 因为访问数据库后, 还要进行很多次的A列表插入操作, 这个插入操作的过程, 很明显还是可以让其他线程继续访问数据库的, 所以B列表的读取处理应该是并行多个线程的, 但A线程就没什么必要进行并行, 因为只简单处理后放到"共享区"而已. 而读取"共享区"的写线程, 看你的服务器速度如何了, 可以多个并行写入, 也可以单行, 视"共享区"的流量而定. 一个线程全速工作就可以完成的话, 没有必要多开线程. 哪里需要并行, 就看你具体的业务需求. 只要抓住一点: 到底一个列表, 是需要多读还是单读, 多写还是单写, 这样线程模型就不会乱, 也不会错. 用实际的业务来代入线程模型来理解是只会带来混乱的. 线程模型假设为数学的话, 业务就是物理. 物理是通过数学手段来解决计算的, 但数学本身, 只代表数字不存在其他意义, 套入物理后才有了实际的意义.
onlycs 2011-12-12
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 dfasri 的回复:]

引用 18 楼 onlycs 的回复:
你可能还没明白我的意思。
读线程是从两个队列中读取数据,根据数据从数据库中取数据。然后把数据库中得数据放入共享区。
写线程是从共享区读取数据,经常处理。并写入另外一个数据库中。

我之前阐述的都是跟读线程有的内容。感觉我对读线程的处理结构比较混乱。


从两个队列中读取数据, 有多少个读线程呢? 这个队列除了被读以外, 难道不用被写就会自动……
[/Quote]
是的,有优先级的。
数据产生:创建线程之前,队列A为空,队列B中放入数据.两个队列的数据不一样的。队列A未ID范围(最大-最小值),队列B为包ID(一个包下面有很多记录ID)
读线程:从队列A取,没有取到。从队列B中取。然后从数据库中取出记录。如果记录很多,将记录很多,把数据重新分包(其实就是简单记录ID范围),放回队列A中。退出。

对于读写线程定义,我是以数据库来区分的。从数据库中读取数据,称之为读线程。反之,为写线程。
dfasri 2011-12-12
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 onlycs 的回复:]
你可能还没明白我的意思。
读线程是从两个队列中读取数据,根据数据从数据库中取数据。然后把数据库中得数据放入共享区。
写线程是从共享区读取数据,经常处理。并写入另外一个数据库中。

我之前阐述的都是跟读线程有的内容。感觉我对读线程的处理结构比较混乱。
[/Quote]

从两个队列中读取数据, 有多少个读线程呢? 这个队列除了被读以外, 难道不用被写就会自动有数据了?
先不管这里你有多少个写, 起码这两个队列均会被多个读线程访问得到, 那么其模型是要求 并行读(多读).
然后读线程要从数据库得出数据, 放入共享区, 这个共享区的模型是要求 并行写(多写)
写线程从共享区读取数据, 然后写入另外一个数据库, 这个模型是要求 单读单写并行, (一读一写)

你对何为读线程, 何为写线程, 是从你的业务角度去考虑的, 然后认为这个线程做读取数据库的就叫读线程, 这个线程写数据库的就叫写线程. 这些对于线程模型来说, 不应该采用这种方式来理解的.

采用MS_Queue, 你的两个被读取队列可以直接综合成一个, 反正是FIFO的, 你的"读线程"可以随便有很多个, 并行直接读取这个Queue即可, 你的共享区, 用MS_Queue之后, 可以随便被"读线程"写入共享数据, "写线程"就可以直接从共享区(即MS_Queue)里面FIFO的形式把数据读取出来, 放入另一个数据库.

有一点很奇怪的是, 为何你非得要用两个队列来让读取线程读呢? 搞优先级别吗?
onlycs 2011-12-10
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 dfasri 的回复:]

这个不是更简单么....
弄两个事件, 其中一个为读取数据的开始信号, 在写入数据可以开始读取的时候, 才会设置, 然后其他读线程可以开始并行读取.
第二个事件为写入完成事件, 由于是边写边读, 所以极有可能写还在继续的时候, 读会被读空了, 但此时判断为没有可读退出线程的话就会错误, 这个事件就是用于读线程进行0时间的等待, 假如成功, 代表写入完成, 读取完成, 假如失败, 代表写还在继……
[/Quote]
你可能还没明白我的意思。
读线程是从两个队列中读取数据,根据数据从数据库中取数据。然后把数据库中得数据放入共享区。
写线程是从共享区读取数据,经常处理。并写入另外一个数据库中。

我之前阐述的都是跟读线程有的内容。感觉我对读线程的处理结构比较混乱。
dfasri 2011-12-09
  • 打赏
  • 举报
回复
这个不是更简单么....
弄两个事件, 其中一个为读取数据的开始信号, 在写入数据可以开始读取的时候, 才会设置, 然后其他读线程可以开始并行读取.
第二个事件为写入完成事件, 由于是边写边读, 所以极有可能写还在继续的时候, 读会被读空了, 但此时判断为没有可读退出线程的话就会错误, 这个事件就是用于读线程进行0时间的等待, 假如成功, 代表写入完成, 读取完成, 假如失败, 代表写还在继续, 要继续循环读取写入的数据.
onlycs 2011-12-08
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 dfasri 的回复:]

引用 12 楼 onlycs 的回复:
关键我这不是单纯的一写多读,会有线程挂起休息的状态。总担心线程被锁死。。

这个跟无锁队列的应用不冲突吧...网上的代码都是无阻塞形式, 也没有休息的形式的, 实际应用中除非真的对效率要求非常高, 有很多个CPU死循环检测并读取列表数据, 一边都要自己加上Event来让读线程在没有数据可以读的时候挂起, 不过也个也是难点之一.
[/Quote]

我现在就是,没有数据时,要休息(挂起),不能直接退出的原因时,当某个线程发现数据太大,它会重新划分数据,并把分好的数据放到队里中。只有当所有线程发现没数据时,才会退出。
dfasri 2011-12-06
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 onlycs 的回复:]
关键我这不是单纯的一写多读,会有线程挂起休息的状态。总担心线程被锁死。。
[/Quote]
这个跟无锁队列的应用不冲突吧...网上的代码都是无阻塞形式, 也没有休息的形式的, 实际应用中除非真的对效率要求非常高, 有很多个CPU死循环检测并读取列表数据, 一边都要自己加上Event来让读线程在没有数据可以读的时候挂起, 不过也个也是难点之一.
onlycs 2011-12-06
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 visualeleven 的回复:]

[/Quote]
部长帮忙指点下呗
Eleven 2011-12-06
  • 打赏
  • 举报
回复
onlycs 2011-12-06
  • 打赏
  • 举报
回复
关键我这不是单纯的一写多读,会有线程挂起休息的状态。总担心线程被锁死。。
dfasri 2011-12-06
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 onlycs 的回复:]
对于你说的‘MS-Queue的流程’,我搜索到,多数是服务器之间通信的,线程之间也可以用吗?
[/Quote]
你搜索错了, 你搜到的是微软的...你搜索 无锁队列 吧..这个是线程间, 不能够跨进程. 用于实现多写多读并行无锁, 比较适合你现有的方式, 但这队列效率不算高, 但由于有代码可以抄..所以很方便.

假如你的只是一写一读的模型, 网上也有很多可以抄, 假如是一写多读的模型, 还是改用MS-Queue最简单了. 在线程运转速度不算太高的情况下, MS-Queue的效率还是可以的.
onlycs 2011-12-06
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 dfasri 的回复:]

你这里并没有清楚描述得到你的线程模型, 只是看起来, 你存在两个队列, 大概是有两个线程会写入.
然后这段函数, 大概是并行读取出来进行处理的函数.

就这段描述来说, 你采用的方式, 是有问题的, 上网查找一下MS-Queue的流程. 我想会对你有帮助.
[/Quote]
两个队列并不是由两个线程写入的。其中队列B数据是在创建线程时传入的。队列A的数据,是处理函数根据一定情况写入得。
对于你说的‘MS-Queue的流程’,我搜索到,多数是服务器之间通信的,线程之间也可以用吗?
dfasri 2011-12-05
  • 打赏
  • 举报
回复
你这里并没有清楚描述得到你的线程模型, 只是看起来, 你存在两个队列, 大概是有两个线程会写入.
然后这段函数, 大概是并行读取出来进行处理的函数.

就这段描述来说, 你采用的方式, 是有问题的, 上网查找一下MS-Queue的流程. 我想会对你有帮助.
onlycs 2011-12-05
  • 打赏
  • 举报
回复
100分页不能吸引人过来哇??
onlycs 2011-12-03
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 sky101010ws 的回复:]

你的性能可能在这里有问题
CANNaviReadResource cReadResource; 初始化成员变量
BOOL bRet = cReadResource.OpenConnect(); //连接数据库
如果成员比较到,则要不停的初始化和析构
还有连接数据库
你最好设置全局变量,这样的效率会提高很多
[/Quote]

对应一个线程来说,只会初始化一次。就是因为连接不能设置全局,才显的代码比较乱。
onlycs 2011-12-03
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 liujie250079934 的回复:]

....你想怎么优化啊 把汉字变成代码????
[/Quote]

代码都有,就是总感觉代码有些乱。想听听各位意见,代码结构可以不可以优化
onlycs 2011-12-02
  • 打赏
  • 举报
回复
重新排下版

UINT ReadResource(LPVOID pParam)
{
InterlockedIncrement(&g_nReadThreadNum);
sReadThreadInit* pRdInit = (sReadThreadInit*)pParam;
CANNaviReadResource cReadResource;
初始化成员变量
BOOL bRet = cReadResource.OpenConnect(); //连接数据库
从全局队列中取数据(两个队列A,B,A中没数据才从B中取
while(bRet)
{
如果两个队列都没数据,线程挂起,并把线程状态设置未挂起

判断退出标志

if(A队列有数据)
{
执行对应函数

if(g_bEndThreadAll || g_nWriteThreadNum == 0) //判断用户发出退出请求或者写线程退出
{
if(当前只要自己处于工作状态)
{
将所有读线程的状态设置为退出;
激活所有线程;
}
break;
}
}
else
{

if(当前只要自己处于工作状态)
{
将所有读线程的状态设置为退出;
激活所有线程;
}
break;

}

从全局队列中取数据(两个队列A,B,A中没数据才从B中取

}
delete pRdInit;
InterlockedDecrement(&g_nReadThreadNum);
return 0;
}
龙行天下之Sky 2011-12-02
  • 打赏
  • 举报
回复
你的性能可能在这里有问题
CANNaviReadResource cReadResource; 初始化成员变量
BOOL bRet = cReadResource.OpenConnect(); //连接数据库
如果成员比较到,则要不停的初始化和析构
还有连接数据库
你最好设置全局变量,这样的效率会提高很多
liujie250079934 2011-12-02
  • 打赏
  • 举报
回复
....你想怎么优化啊 把汉字变成代码????
加载更多回复(1)

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧