c 来写的 应该用什么好?
ace 里面有吗? 线程安全吗?
--
Take care
Stone Jiang
WaitFor也同样会挂起线程,而且1ms的睡眠并不影响你的响应。考虑网络延时一般都是100ms到600ms,对比一下,真的是无所谓,反而更处其它处理更多的CPU机会和时间。
至于线程安全问题,这个和是否睡眠没有关系。是多线程竞争资源必须做的工作。(不过,对于多线程,推荐如果不是很了解,还是不要乱用的好;当然,用的好,可以获得很多好处。)
说真的,我看不出这个ACE封装相对API有什么好处(反而是更低效),winAPI是Sleep(
unsingned ms ),unix系(包括linux)的是sleep( unsingned
ms,unsigned us
)(这里unsigned记得不是很确切,也可能是int,如果是int,我到觉得他应该是unsigned的好)
----- Original Message -----From: Stone JiangSent: Monday, May 08, 2006 4:03 PMSubject: Re: 要在多线程里面延时1S sleep 好像不是安全的
你谈的是Facade的好处,而不是肤浅封装的好处。
我想用ACE的程序员,恐怕不会对win或linux平台有所了解,至少他会熟悉其中之一,而熟悉之一,再熟悉另一,我看ACE这个API封装没有带来什么优势——
至少肤浅的封装他不应该企图去做一个无效的转换。
请注意,我只是谈ACE这个API的封装设计。——
至少,这个API,我不敢苟同他的设计。
至于一般的说法,我想搁下看来从来不浪费CPU资源的了,没事空转也要让他100%,还好微软MFC做了些工作。我真怀疑,我机器除了跑搁下的程序,会什么事都不能做了。
如果要获取精确的时间用timeGetTime(可以精确到ms)或QueryPerfermanceCounter
请注意,我只是看到了这个sleep的封装,不太赞同。别的封装,因为并不太了解,也不敢多言。
或许,他的整体设计需求,这样封装,符合他的整体性,那么,也就是说他的这个整体封装设计思路还不够好。
至于性能,多以个调用开销,倒也没什么。
主要是构造、析构、copy构造、析构。即使编译器为你优化,也还是有至少一次构造、析构。
看你的回答 基本能明白你的水平
00999 ACE_INLINE int
01000 ACE_OS::sleep (u_int seconds)
01001 {
01002 ACE_OS_TRACE ("ACE_OS::sleep");
01003 #if defined (ACE_WIN32)
01004 ::Sleep (seconds * ACE_ONE_SECOND_IN_MSECS);
01005 return 0;
01006 #if 0
01007 #elif defined (HPUX_10) && defined (ACE_HAS_PTHREADS_DRAFT4)
01008 // On HP-UX 10, _CMA_NOWRAPPERS_ disables the mapping from sleep
to cma_sleep
01009 // which makes sleep() put the whole process to sleep, and keeps
it from
01010 // noticing pending cancels. So, in this case, use pthread_delay_np
01011 struct timespec rqtp;
01012 rqtp.tv_sec = seconds;
01013 rqtp.tv_nsec = 0L;
01014 return pthread_delay_np (&rqtp);
01015 #endif /* 0 */
01016 #elif defined (ACE_HAS_CLOCK_GETTIME)
01017 struct timespec rqtp;
01018 // Initializer doesn't work with Green Hills 1.8.7
01019 rqtp.tv_sec = seconds;
01020 rqtp.tv_nsec = 0L;
01021 ACE_OSCALL_RETURN (::nanosleep (&rqtp, 0), int, -1);
01022 #elif defined (ACE_PSOS)
01023 timeval wait;
01024 wait.tv_sec = seconds;
01025 wait.tv_usec = 0;
01026 ACE_OSCALL_RETURN (::select (0, 0, 0, 0, &wait), int, -1);
01027 #else
01028 ACE_OSCALL_RETURN (::sleep (seconds), int, -1);
01029 #endif /* ACE_WIN32 */
01030 }
01031
01032 ACE_INLINE int
01033 ACE_OS::sleep (const ACE_Time_Value &tv)
01034 {
01035 ACE_OS_TRACE ("ACE_OS::sleep");
01036 #if defined (ACE_WIN32)
01037 ::Sleep (tv.msec ());
01038 return 0;
01039 #else
01040 # if defined (ACE_HAS_NONCONST_SELECT_TIMEVAL)
01041 // Copy the timeval, because this platform doesn't declare the
timeval
01042 // as a pointer to const.
01043 timeval tv_copy = tv;
01044 ACE_OSCALL_RETURN (::select (0, 0, 0, 0, &tv_copy), int, -1);
01045 # else /* ! ACE_HAS_NONCONST_SELECT_TIMEVAL */
01046 const timeval *tvp = tv;
01047 ACE_OSCALL_RETURN (::select (0, 0, 0, 0, tvp), int, -1);
01048 # endif /* ACE_HAS_NONCONST_SELECT_TIMEVAL */
01049 #endif /* ACE_WIN32 */
01050 }
6.
严格来说,因为是sleep,所以这里的函数调用开销是可以忽略的,没有任何的所谓性能影响,但,它形成的是一种不好习惯和对性能问题的忽视——
有些时候是可以需要在意的。
7. 不是大家觉得不够好,是我个人觉得(^_^)。
8.
头文件里包含代码和一堆宏,确实是比较的麻烦。比如STLport的STL实现,虽然号称是可读性最好的,但是,读取来是相当痛苦的事。
9. 我倾向第一个提供方式,因为概念更少。
第二个引入了一个中间概念(本身可以对用户忽略的概念)。
我水平是也很一般,这个不瞒你说,我自己知道。
说真的,我对sunway的说法只不过是加重语气、强调罢了,如果是本人觉得郁闷,我也可以理解和接受。而偏偏搁下这个,我就很为反感了。——
还好,我们不需要成为朋友。
以前,对这种郁闷惯了,置之不理。但我本来就是小心眼的人,对这种人,今天,我就偏不客气了。
#include <windows.h>
#pragma comment( lib,"winmm" )
int main( int argc,char *argv[] )
{
timeBeginPeriod( 1 );
::Sleep( 100 );
for ( int i = 0; i < 10; ++i )
{
unsigned start = timeGetTime();
::Sleep( 1 );
unsigned end = timeGetTime();
std::cout << "time elapsed : " << end - start << std::endl;
}
timeEndPeriod( 1 );
return 0;
}
运行一下,我机器上(win2K),结果显示是:
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
time elapsed : 2
不过,有的时候确实会上16ms,这个是平均结果。
GetTickCount的精度只有10ms,如果要获得更精确的时间,还是不能用它的。
inline为什么不inline,是因为
1. virtual 函数无法inline
2.
inline可能使得代码量增大,从而导致CPU缓存丢失,反而降低性能
—— 比如,一般对循环的inline都会被忽略。
3.
inline本来就是为了速度考虑,所以,MS选择不一定给予inline,因为它不一定能提高性能。——
这个是我想MS这么做的原因。
我的工作的平台确实是win,而且我本人对linux也不怎么了解。
我想他的版本应该比你高,因为做为const
&好像很应该也很正常。
当然,我知道linux下也有个struct的sleep版本,你不觉得ACE这样没有对你屏蔽底层平台吗?而是每个平台提供了一套方案。还好他们的原理都一直,所以理解起来并不是很困难。既然这样,为什么不直接学习平台API呢(和你学习ACE的代价等同了,我指这个API封装)?而且你可以定制你自己的需求(而ACE这里相对定制能力就要差一些了)。
而是复杂化了问题。难道,你提供封装接口是这样提供的?你赞同这样做?如果你回答肯定,...
;如果否定,我要表达的意思已经很明确 ——
为什么我觉得它这里不好(或不够好)。
2. 还有,我只是顺便帮你解释为什么inline只是建议。
3. #define
和inline根本就不是一回事,inline不等于#define,这个我就不多说了。
4. 我要表达的都写的清清楚楚,已经没什么好说的。
5.
我不是要和你争论个什么结论出来,我只是顺便,给些值得参考的意见罢了。你听的进就听,听不进就不听。
6.
我还并没有说我比ACE做的好,你要搞清楚,我每处的表述。——
和fans们说话,就是累。就如我会情绪化一样,我也很理解fans们的情绪化。
7.
好了,我不多说了。不过,你贴出来的ACE代码,写的还不错。感谢你告诉了我linux下有nanosleep等。
Darwin Lalo 写道:
> 谁说的,晕
> 你们自己运行看看
>
> DWORD tc[100];
> for(int i = 0; i < sizeof(tc)/sizeof(DWORD); ++i){
> tc[i] = GetTickCount();
> }
>
> for(int i = 0; i < sizeof(tc)/sizeof(DWORD); ++i){
> printf("%d\n", tc[i]);
> }
>
>
> sleep(1) 不可靠,我认可,但是。。。
>
> 要想搞清楚这个问题,请看看 操作系统->调度算法
> windows 的时间片轮转 什么的
>
>
> sunway 写道:
java的效率会比c/c++的程序慢一倍,这个还算是很理想的数据,但是如果是高负载高能服务器,你不见得能把硬件的档次提高一倍。更何况java的NIO低层目前也都还没有能力粘和IOCP和POSIX
AIO,定多就是select和epoll在5.0的版本里。
> —— 至少针对我特殊的需求更适应。
>
> 你谈的是Facade的好处,而不是肤浅封装的好处。
>
> 我想用ACE的程序员,恐怕不会对win或linux平台有所了解,至少他会熟悉其中之一,而熟悉之一,再熟悉另一,我看ACE这个API封装没有带来什么优势——
> 至少肤浅的封装他不应该企图去做一个无效的转换。
>
> 请注意,我只是谈ACE这个API的封装设计。——
> 至少,这个API,我不敢苟同他的设计。
如果你做跨平台的高能服务器,或者框架,请给出你的设计和思路?
>
> >
>
--
Enjoy your life! Make things clearly and easier!
Reply Mailto: alvi...@gmail.com
My Blog: http://wolf.bloghome.cn
请看《Windows核心编程》里面介绍线程切换的章节讲的很清楚,由于NT内核系统并不是设计为实时操作系统,线程调度的时间精度是30ms。
>
> 至于一般的说法,我想搁下看来从来不浪费CPU资源的了,没事空转也要让他100%,还好微软MFC做了些工作。我真怀疑,我机器除了跑搁下的程序,会什么事都不能做了。
这就是你的不对了,就算他不知道,你就不能讲解出来,更何况自己都搞清除了没?还就是只搞清楚了MFC的那点封装类,更何况其他平台以及操作系统理论。
就ACE_Time_Value不光是只给sleep用的,ACE中的时间和超时操作基本上都是依赖于
它的,想想Timer,IO超时吧。
>
> 或许,他的整体设计需求,这样封装,符合他的整体性,那么,也就是说他的这个整体封装设计思路还不够好。
>
>
> 至于性能,多以个调用开销,倒也没什么。
> 主要是构造、析构、copy构造、析构。即使编译器为你优化,也还是有至少一次构造、析构。
ACE_Time_Value和《C++对象模型》你都仔细看了吗?还只是略知皮毛?ACE的ACE_Time_Value根本就是个POD,你要要觉得效率低,那直接使用memcpy好了。关于构造,你也说了多个调用开销也没什么,如果真是C++加上的"无用"的空构造和析构,就算调用一下也如你说的没什么,更何况空调用会被编译器优化掉。另外,就算是操作系统提供的时间结构,你不也要使用memset初始化一下的。
有二个参数,一个是秒,另外一个是毫秒,这样使用起来方便。而不是在使用秒的时候给一个巨大的数字。
> 3. 你用的const ACE_Time_Value
> &tv和前面那位仁兄贴的不太一样吧!
> 4. ACE_Time_Value
> 是一个struct吧?C++里,这个struct的生成是有构造开销的(可能他这里什么都没做)。
错了,是class,你前面和后面也说了,函数调用开销是可以忽略,那在乎一个构造的函数调用有什么意义。
> 5. 他为什么要提供select阻塞的呢?蛮奇怪的。
在win32下是sleep,在其他平台是用select的超时做的。
>
> 6.
> 严格来说,因为是sleep,所以这里的函数调用开销是可以忽略的,没有任何的所谓性能影响,但,它形成的是一种不好习惯和对性能问题的忽视——
> 有些时候是可以需要在意的。
> 7. 不是大家觉得不够好,是我个人觉得(^_^)。
> 8.
> 头文件里包含代码和一堆宏,确实是比较的麻烦。比如STLport的STL实现,虽然号称是可读性最好的,但是,读取来是相当痛苦的事。
> 9. 我倾向第一个提供方式,因为概念更少。
> 第二个引入了一个中间概念(本身可以对用户忽略的概念)。
>
> >
>
这个《c++对象模型》上应该是可以査到的。
> 2。可能是你工作的平台API是基于ms的吧,因为是ms的平台:)
> Linux下可不是这样,高精度的是另外一个函数nanosleep来实现的。ACE按照Linux
> 的做法把它们分开了。所以还提供了一个 nanosleep函数。
> 3。可能我和他源码版本不同造成的,我的是5.4
> 4。一秒钟能掉几次sleep?这种都要算作性能瓶颈的理由,太牵强了。难道真的要
> 回归c的时代。c里面也用struct:)
> 5。这个问题的重点在于ACE_PSOS的定义,我没有用过这方面的东西,懂得人请站
> 出来解释一下。个人猜测可能是在那个平台或者环境下面没有合适的 sleep,所以
> 简单的模拟一下。
我也是这么想的,用超时来模拟。
ACE有几个层次,ACE_OS::sleep只是OS API的屏蔽层,那上面的框架层,服务层,按你这么说就用OS
API的屏蔽层就能说明上面主要层次的问题了吗?更何况,ACE主要的用途也就是框架层和服务层。当你要使用API时,要屏蔽操作系统差异时,也可以用它为你准备好的ACE_OS层的API,那样不更方便,更何况可以和框架保良好的统一,不是吗?更妙的时候它能够很好的和框架的对象交互。何乐而不为?
>
> 而是复杂化了问题。难道,你提供封装接口是这样提供的?你赞同这样做?如果你回答肯定,...
> ;如果否定,我要表达的意思已经很明确 ——
> 为什么我觉得它这里不好(或不够好)。
>
> >
>
2006/5/9, 清风雨 <zhang...@gmail.com>:
你不要把你的以为的盖到我头上,我本来是不想和你们多说什么了。因为,我发现,一路在歪曲我的意思。
不是看的书多,理解的就深刻,虽然看书很重要很重要。
再则,这个问题二楼本来都说的很清楚了。Sleep本来就是个系统调用,根本都没资源冲突,都谈不上冲突。我认为楼主是想用Sleep来同步资源访问,那肯定是不太可行的方法。这个贴讨论这么多,引入些新的话题,大家讨论下也无妨啊。
你的帖子我都认真看了,都花费了一个小时时间,发表点意见,你不会不接受吧。
最后再补充一点,“封装”并不就等于“低效”,或者是等同于“不如你自己手工编写
的代码”,即使是汇编高手,也不能说自己就比编译器做得好。
正如阁下对ACE中sleep的误解一样。
另外,用MFC开发程序还是很方便的,像vb,拖拖拽拽,补充几个方法也能对付对
付了,比WTL方便。当然,我们组只是用它作一些辅助开发的小工具,并不是做产品。
要是高兴的话,我可以把WIN下面Sleep的原代码贴一下。
--
Closeall
MSN : clos...@hotmail.com
Google Talk : closea...@gmail.com
/*++
Routine Description:
This function delays the execution of the current thread for the
specified
interval of time.
Arguments:
WaitMode - Supplies the processor mode in which the delay is to
occur.
Alertable - Supplies a boolean value that specifies whether the
delay
is alertable.
Interval - Supplies a pointer to the absolute or relative time over
which
the delay is to occur.
Return Value:
The wait completion status. A value of STATUS_SUCCESS is returned
if
the delay occurred. A value of STATUS_ALERTED is returned if the
wait
was aborted to deliver an alert to the current thread. A value of
STATUS_USER_APC is returned if the wait was aborted to deliver a
user
APC to the current thread.
--*/
{
LARGE_INTEGER DueTime;
LARGE_INTEGER NewTime;
PLARGE_INTEGER OriginalTime;
PKPRCB Prcb;
KPRIORITY Priority;
PRKQUEUE Queue;
PRKTHREAD Thread;
PRKTIMER Timer;
PKWAIT_BLOCK WaitBlock;
NTSTATUS WaitStatus;
//
// If the dispatcher database lock is not already held, then set
the wait
// IRQL and lock the dispatcher database. Else set boolean wait
variable
// to FALSE.
//
Thread = KeGetCurrentThread();
if (Thread->WaitNext) {
Thread->WaitNext = FALSE;
} else {
KiLockDispatcherDatabase(&Thread->WaitIrql);
}
//
// Start of delay loop.
//
// Note this loop is repeated if a kernel APC is delivered in the
middle
// of the delay or a kernel APC is pending on the first attempt
through
// the loop.
//
OriginalTime = Interval;
WaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
do {
//
// Test to determine if a kernel APC is pending.
//
// If a kernel APC is pending and the previous IRQL was less
than
// APC_LEVEL, then a kernel APC was queued by another processor
just
// after IRQL was raised to DISPATCH_LEVEL, but before the
dispatcher
// database was locked.
//
// N.B. that this can only happen in a multiprocessor system.
//
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql <
APC_LEVEL)) {
//
// Unlock the dispatcher database and lower IRQL to its
previous
// value. An APC interrupt will immediately occur which
will result
// in the delivery of the kernel APC if possible.
//
KiUnlockDispatcherDatabase(Thread->WaitIrql);
} else {
//
// Test for alert pending.
//
TestForAlertPending(Alertable);
//
// Initialize wait block, insert wait block in timer wait
list,
// insert timer in timer queue, put thread in wait state,
select
// next thread to execute, and context switch to next
thread.
//
// N.B. The timer wait block is initialized when the
respective
// thread is initialized. Thus the constant fields are
not
// reinitialized. These include the wait object, wait
key,
// wait type, and the wait list entry link pointers.
//
Thread->WaitBlockList = WaitBlock;
Thread->WaitStatus = (NTSTATUS)0;
Timer = &Thread->Timer;
WaitBlock->NextWaitBlock = WaitBlock;
Timer->Header.WaitListHead.Flink =
&WaitBlock->WaitListEntry;
Timer->Header.WaitListHead.Blink =
&WaitBlock->WaitListEntry;
//
// If the timer is inserted in the timer tree, then place
the
// current thread in a wait state. Otherwise, attempt to
force
// the current thread to yield the processor to another
thread.
//
if (KiInsertTreeTimer(Timer, *Interval) == FALSE) {
//
// If the thread is not a realtime thread, then drop
the
// thread priority to the base priority.
//
Prcb = KeGetCurrentPrcb();
Priority = Thread->Priority;
if (Priority < LOW_REALTIME_PRIORITY) {
if (Priority != Thread->BasePriority) {
Thread->PriorityDecrement = 0;
KiSetPriorityThread(Thread,
Thread->BasePriority);
}
}
//
// If a new thread has not been selected, the attempt
to round
// robin the thread with other threads at the same
priority.
//
if (Prcb->NextThread == NULL) {
Prcb->NextThread =
KiFindReadyThread(Thread->NextProcessor,
Thread->Priority);
}
//
// If a new thread has been selected for execution,
then
// switch immediately to the selected thread.
//
if (Prcb->NextThread != NULL) {
//
// Give the current thread a new qunatum and switch
// context to selected thread.
//
// N.B. Control is returned at the original IRQL.
//
Thread->Preempted = FALSE;
Thread->Quantum =
Thread->ApcState.Process->ThreadQuantum;
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
KiReadyThread(Thread);
WaitStatus = (NTSTATUS)KiSwapThread();
goto WaitComplete;
} else {
WaitStatus = (NTSTATUS)STATUS_SUCCESS;
break;
}
}
DueTime.QuadPart = Timer->DueTime.QuadPart;
//
// If the current thread is processing a queue entry, then
attempt
// to activate another thread that is blocked on the queue
object.
//
Queue = Thread->Queue;
if (Queue != NULL) {
KiActivateWaiterQueue(Queue);
}
//
// Set the thread wait parameters, set the thread
dispatcher state
// to Waiting, and insert the thread in the wait list.
//
Thread->Alertable = Alertable;
Thread->WaitMode = WaitMode;
Thread->WaitReason = DelayExecution;
Thread->WaitTime= KiQueryLowTickCount();
Thread->State = Waiting;
KiInsertWaitList(WaitMode, Thread);
//
// Switch context to selected thread.
//
// N.B. Control is returned at the original IRQL.
//
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
WaitStatus = (NTSTATUS)KiSwapThread();
//
// If the thread was not awakened to deliver a kernel mode
APC,
// then return the wait status.
//
WaitComplete:
if (WaitStatus != STATUS_KERNEL_APC) {
if (WaitStatus == STATUS_TIMEOUT) {
WaitStatus = STATUS_SUCCESS;
}
return WaitStatus;
}
//
// Reduce the time remaining before the time delay expires.
//
Interval = KiComputeWaitInterval(OriginalTime,
&DueTime,
&NewTime);
}
//
// Raise IRQL to DISPATCH_LEVEL and lock the dispatcher
database.
//
KiLockDispatcherDatabase(&Thread->WaitIrql);
} while (TRUE);
//
// The thread is alerted or a user APC should be delivered. Unlock
the
// dispatcher database, lower IRQL to its previous value, and
return the
// wait status.
//
KiUnlockDispatcherDatabase(Thread->WaitIrql);
return WaitStatus;
}
PLARGE_INTEGER
FASTCALL
KiComputeWaitInterval (
IN PLARGE_INTEGER OriginalTime,
IN PLARGE_INTEGER DueTime,
IN OUT PLARGE_INTEGER NewTime
)
/*++
Routine Description:
This function recomputes the wait interval after a thread has been
awakened to deliver a kernel APC.
Arguments:
OriginalTime - Supplies a pointer to the original timeout value.
DueTime - Supplies a pointer to the previous due time.
NewTime - Supplies a pointer to a variable that receives the
recomputed wait interval.
Return Value:
A pointer to the new time is returned as the function value.
--*/
{
//
// If the original wait time was absolute, then return the same
// absolute time. Otherwise, reduce the wait time remaining before
// the time delay expires.
//
if (OriginalTime->QuadPart >= 0) {
return OriginalTime;
} else {
KiQueryInterruptTime(NewTime);
NewTime->QuadPart -= DueTime->QuadPart;
return NewTime;
}
}
-----邮件原件-----
发件人: dev4s...@googlegroups.com [mailto:dev4s...@googlegroups.com] 代
表 SevenCat
发送时间: 2006年5月13日 14:22
收件人: 高性能网络编程邮件列表
主题: Re: 要在多线程里面延时1S sleep 好像不是安全的