询问关于线程和定时器的区别

蜚语丶 2016-03-11 08:55:19
加精
线程和定时器到底有什么区别,定时器应该不是新开的一条线程,我想知道线程和定时器的实现原理,更深入的基本原理,能够看出两者本质的差异在哪,请问有人知道吗?

还有为什么在做界面的时候,不要用新开的线程来控制窗体组件,我们老师说这不合适,窗体组件应该都由主线程控制,为什么?
...全文
3970 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2016-03-20
  • 打赏
  • 举报
回复
你可以研究一下,VC生成的WIN32窗口程序 其中,有一个消息循环 一个窗口函数 这是应用程序代码的主体 其他代码,都是为这两部分做准备的 窗口函数 是具体响应消息的函数 消息循环是整个窗口系统代码,运行的核心程序(核心流程)。 它是一直在运行,直到关闭窗口为止。 这是和控制台程序不同的,控制台程序,按照代码顺序执行,到结束为止。 不一定会一直循环运行 窗口程序 一定会有个循环在一直执行的。
lm_whales 2016-03-20
  • 打赏
  • 举报
回复
消息的发送是操作系统处理的,如何处理消息,是应用程序处理的 消息循环,就是应用程序, 不断的在系统的管理程序手中, 领回跟自己相关消息(调用 GetMessage 函数),并调用系统函数API 转换,分发和自己的消息 分发消息API,会调用会调用窗口类的回调函数,窗口函数 窗口函数中,处理 WM_TIMER的代码,会处理 定时器消息 也就是说,消息循环,和窗口函数,一起处理 应用程序的消息
lm_whales 2016-03-20
  • 打赏
  • 举报
回复
不是检查时间,而是定时器时间到了(通过硬件中断和操作系统以及你设置的定时器参数,共同作用的结果) 会发送一个 WM_TIMER 消息 这个消息,和 其他消息一样,被应用程序的消息循环,和窗口函数等回调函数处理 消息循环,以及应用程序的窗口函数,这些回调函数,都是程序自己定义的, 是在某个线程中运行的(通常是界面线程,多半是主线程)
善良超锅锅 2016-03-20
  • 打赏
  • 举报
回复
引用 11 楼 lm_whales 的回复:
线程和定时器的调度方法不同 线程开始后,随时可以挂起,让其它线程运行,等待下次调度,以便继续执行,因此可以长期运行。 定时器没启动一次,执行一段代码,不可执行长期。长期运行会造成定时器阻塞。 线程是单独调度的,定时器只是打断线程的执行,暂时插入运行定时器的代码 没有定时器,线程是这样执行的(不考虑线程调度的影响) 线程--------------------------------------------------------------------------------------------> 有了定时器,线程运行中,会插入执行定时器的代码 定时器 |=========>| |=========>| 每次时间到了执行一次定时器的代码 线程--------+ +--------------+ +-------------------------------> 线程切换是在指令级别切换 ,被打断的是指令执行序列, 定时器每次执行是一个完整的函数调用。你写的代码,会完整的执行一次。 本质上还是不同的。 至于线程调度的时间片轮转算法,确实和定时器相关,但那是系统硬件定时器。 应用程序中的定时器,是软件定时器,是定时器硬件和操作系统联合起来虚拟出的定时器。
可不可以这样理解:当你设置了定时器后,每次消息循环时都要检查一下时间,如果设定的时间到就往消息队列里插入一个WM_TIMER消息。这样看,定时器的逻辑还是在主线程中的。 如果是这样的,就可以理解WM_TIMER的不精确性,因为你不知道每次消息循环话的时间是多长,而且不同的消息处理时间可能不一样。 是这样的吗?
kuankuan_qiao 2016-03-18
  • 打赏
  • 举报
回复
eyerr 2016-03-17
  • 打赏
  • 举报
回复
其实定时器本身也是运行在一个线程里 - 即界面线程。另外线程也是可以用来实现定时器的。 至于为什么最好不要在工作线程里直接操作界面,aardio教程里有个解释是:多线程就象多个并列奔驰的火车,你要在一个飞驰的火车上控制另一个火车上的对象,直接伸手过去操作很危险,并且没有必要,让多个线程去操作界面线程的对象,这就相当于你把火车上的人都喊下车了,张三抱个轮子往前跑,李四抱个车厢往前跑,这并不能让你这部火车跑的比其他火车更快,一段aardio代码演示响应式的控制界面对象:
import win.ui;
/*DSG{{*/
var winform = win.form(text="线程命令";right=599;bottom=399)
winform.add(
edit={cls="edit";left=12;top=11;right=588;bottom=389;db=1;dl=1;dr=1;dt=1;edge=1;multiline=1;z=1}
)
/*}}*/

import thread.command;
var listener = thread.command();
listener.print = function( ... ){
    winform.edit.print( ... ) //我们在界面线程中这样响应工作线程的消息
} 

//创建工作线程
thread.invoke(

    function(){ 
    
        import thread.command;
        
        //调用界面线程的命令
        thread.command.print("hello world",1,2,3); 
        
    } 
)


winform.show();
win.loopMessage();
qq_22934047 2016-03-16
  • 打赏
  • 举报
回复
完全没有关系的两个东西,多看书
ooolinux 2016-03-15
  • 打赏
  • 举报
回复
同意楼上,关于进程和线程楼主可以看一下《操作系统》的课本。
haitao_J 2016-03-15
  • 打赏
  • 举报
回复
第一个问题楼上已经有很多大神回答了。 第二个问题我觉得是这样的。线程A创建的界面,线程B操作界面控件,一般不会出问题。但由于线程是随时可中断的,可能线程A操作一个控件C的过程中,系统切换线程到B,B此时操作了控件C,之后线程切换为A继续操作控件C,而此时C的状态已被线程B改变了,这样就会报错了。
blueink_200451 2016-03-14
  • 打赏
  • 举报
回复
路过学习了。这个其实之前自己也不是很懂。楼主好问题,辛苦了。
qq_31212173 2016-03-13
  • 打赏
  • 举报
回复
顶一个
cattpon 2016-03-12
  • 打赏
  • 举报
回复
learning~
lm_whales 2016-03-12
  • 打赏
  • 举报
回复
线程和定时器的调度方法不同 线程开始后,随时可以挂起,让其它线程运行,等待下次调度,以便继续执行,因此可以长期运行。 定时器没启动一次,执行一段代码,不可执行长期。长期运行会造成定时器阻塞。 线程是单独调度的,定时器只是打断线程的执行,暂时插入运行定时器的代码 没有定时器,线程是这样执行的(不考虑线程调度的影响) 线程--------------------------------------------------------------------------------------------> 有了定时器,线程运行中,会插入执行定时器的代码 定时器 |=========>| |=========>| 每次时间到了执行一次定时器的代码 线程--------+ +--------------+ +-------------------------------> 线程切换是在指令级别切换 ,被打断的是指令执行序列, 定时器每次执行是一个完整的函数调用。你写的代码,会完整的执行一次。 本质上还是不同的。 至于线程调度的时间片轮转算法,确实和定时器相关,但那是系统硬件定时器。 应用程序中的定时器,是软件定时器,是定时器硬件和操作系统联合起来虚拟出的定时器。
renwotao2009 2016-03-12
  • 打赏
  • 举报
回复
系统内核支持定时器设置,与框架无关
ooolinux 2016-03-12
  • 打赏
  • 举报
回复
引用 5 楼 a19941204cwj 的回复:
[quote=引用 2 楼 u012879787 的回复:] 线程掉windows的调度单位, 定时器是WM_TIMER一种消息 windows有有消息队列,分系统消息队列,线程消息队列。 主线程创建了窗口,主线程维护一个消息队列。 工作线程一般是通过发送消息给主线程,让主线程去操作界面,理由是—————— mfc 的窗口指针不能跨线程!!!! 一旦跨线程就崩溃。 那么不用mfc,直接用win32 api写界面,可以操作界面否? 可以!!!!
我是用C++ Builder来写窗体的,没用到MFC,所以我实际上用新开的线程来控制窗体组件并没有问题,但为什么这样子我们老师还说不行?最好不好这样[/quote] 书上说的: 当程序中使用VCL对象时,它们的属性和方法并不保证对线程是安全的,也就是说,访问其属性或执行其方法可能会执行一些使用内存的动作,但却没有保护这些内存不为其它线程的动作所用。因此,一般利用主线程访问VCL对象。主线程是一个处理应用程序中组件收到的所有Windows消息的线程。 请问楼主是什么学校的,你们有开C++ Builder的课吗,还是老师有要求用C++ Builder?
ooolinux 2016-03-12
  • 打赏
  • 举报
回复
我百度了一篇资料《WM_TIMER消息映射》,这样写到: SetTimer函数用于创建一个计时器,KillTimer函数用于销毁一个计时器。计时器属于系统资源,使用完应及时销毁。 SetTimer的函数原型如下: UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc ) ; 系统将向应用程序队列按照指定的时间间隔(不严格精确)发送WM_TIMER消息。 关于WM_TIMER消息 wParam为计时器的ID。lParam为指向TimerProc的指针,如果调用SetTimer时没有指定TimerProc(参数值为NULL),则lParam为0(即NULL)。 情况(一) lpTimerFunc参数不为NULL而指定为TimerProc函数的指针。这种方法使用TimerProc函数(名字可自定)处理WM_TIMER消息: VOID CALLBACK TimerProc ( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) { //处理WM_TIMER讯息 } DefWindowProc将在处理WM_TIMER消息时调用这个lpTimerFunc所指向的回调函数。 情况(二) lpTimerFunc置NULL从而不使用TimerProc,在窗口过程中处理WM_TIMER消息,比如: 使用多个计时器 使用多个计时器只要在建立计时器时指定不同的ID。 #define TIMER_SEC 1 #define TIMER_MIN 2 然后使用两个SetTimer来设定两个计时器: SetTimer (hwnd, TIMER_SEC, 1000, NULL) ; SetTimer (hwnd, TIMER_MIN, 60000, NULL) ; WM_TIMER的处理如下所示: case WM_TIMER: switch (wParam) { case TIMER_SEC: //每秒一次的处理 break ; case TIMER_MIN: //每分钟一次的处理 break ; }
赵4老师 2016-03-11
  • 打赏
  • 举报
回复
#pragma comment(lib,"user32")
#include <stdio.h>
#include <time.h>
#include <sys/timeb.h>
#include <windows.h>
char datestr[16];
char timestr[16];
char mss[4];
void log(char *s) {
    struct tm *now;
    struct timeb tb;

    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,s);
}
VOID CALLBACK myTimerProc1(
  HWND hwnd, // handle of window for timer messages
  UINT uMsg, // WM_TIMER message
  UINT idEvent, // timer identifier
  DWORD dwTime // current system time
) {
 log("In myTimerProc1\n");
}
VOID CALLBACK myTimerProc2(
  HWND hwnd, // handle of window for timer messages
  UINT uMsg, // WM_TIMER message
  UINT idEvent, // timer identifier
  DWORD dwTime // current system time
) {
 log("In myTimerProc2\n");
}
int main() {
    int i;
    MSG msg;

    SetTimer(NULL,0,1000,myTimerProc1);
    SetTimer(NULL,0,2000,myTimerProc2);
    for (i=0;i<20;i++) {
        Sleep(500);
        log("In main\n");
        if (GetMessage(&msg,NULL,0,0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

    }
    return 0;
}
//2012-07-26 17:29:06.375 In main
//2012-07-26 17:29:06.875 In myTimerProc1
//2012-07-26 17:29:07.375 In main
//2012-07-26 17:29:07.875 In myTimerProc2
//2012-07-26 17:29:08.375 In main
//2012-07-26 17:29:08.375 In myTimerProc1
//2012-07-26 17:29:08.875 In main
//2012-07-26 17:29:08.875 In myTimerProc1
//2012-07-26 17:29:09.375 In main
//2012-07-26 17:29:09.890 In myTimerProc2
//2012-07-26 17:29:10.390 In main
//2012-07-26 17:29:10.390 In myTimerProc1
//2012-07-26 17:29:10.890 In main
//2012-07-26 17:29:10.890 In myTimerProc1
//2012-07-26 17:29:11.390 In main
//2012-07-26 17:29:11.890 In myTimerProc2
//2012-07-26 17:29:12.390 In main
//2012-07-26 17:29:12.390 In myTimerProc1
//2012-07-26 17:29:12.890 In main
//2012-07-26 17:29:12.890 In myTimerProc1
//2012-07-26 17:29:13.390 In main
//2012-07-26 17:29:13.890 In myTimerProc2
//2012-07-26 17:29:14.390 In main
//2012-07-26 17:29:14.390 In myTimerProc1
//2012-07-26 17:29:14.890 In main
//2012-07-26 17:29:14.890 In myTimerProc1
//2012-07-26 17:29:15.390 In main
//2012-07-26 17:29:15.890 In myTimerProc2
//2012-07-26 17:29:16.390 In main
//2012-07-26 17:29:16.390 In myTimerProc1
//2012-07-26 17:29:16.890 In main
//2012-07-26 17:29:16.890 In myTimerProc1
//2012-07-26 17:29:17.390 In main
//2012-07-26 17:29:17.890 In myTimerProc2
//2012-07-26 17:29:18.390 In main
//2012-07-26 17:29:18.390 In myTimerProc1
//2012-07-26 17:29:18.890 In main
//2012-07-26 17:29:18.890 In myTimerProc1
//2012-07-26 17:29:19.390 In main
//2012-07-26 17:29:19.890 In myTimerProc2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <io.h>
    #include <process.h>
    #define  MYVOID             void
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  _vsnprintf         vsnprintf
    #define  MYVOID             void *
#endif
//Log{
#define MAXLOGSIZE 20000000
#define ARRSIZE(x) (sizeof(x)/sizeof(x[0]))
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
char logstr[16000];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
void sleep_ms(int ms) {
    Sleep(ms);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
void sleep_ms(int ms) {
    usleep(ms*1000);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;


    if (NULL==pszFmt||0==pszFmt[0]) return;
    if (-1==_vsnprintf(logstr,ARRSIZE(logstr),pszFmt,argp)) logstr[ARRSIZE(logstr)-1]=0;
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
            flog=fopen(logfilename1,"a");
            if (NULL==flog) return;
        }
        fclose(flog);
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
int No_Loop=0;
MYVOID testThread(void *pcn) {
    int n,i;

    n=(int)pcn;
    i=0;
    while (1) {
        sleep_ms(1000);
        Log("in testThread %d:i==%ds\n",n,++i);
        if (i>=5) No_Loop=1;
    }
}
int main(int argc,char * argv[]) {
    int i;
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
#else
    pthread_mutex_init(&cs_log,NULL);
    pthread_t threads[1];
    int threadsN;
    int rc;
#endif
    Log("=========BEGIN==================\n");
#ifdef WIN32
    _beginthread((void(__cdecl *)(void *))testThread,0,(void *)1);
#else
    threadsN=0;
    rc=pthread_create(&(threads[threadsN++]),NULL,testThread,(void *)1);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1);
#endif
    i=0;
    while (1) {
        sleep_ms(100);
        Log("in main:i==%d\n",++i);
        if (No_Loop==1) break;//
    }
    Log("=========END====================\n");
#ifdef WIN32
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}
//2012-06-14 16:27:21.500 =========BEGIN==================
//2012-06-14 16:27:21.609 in main:i==1
//2012-06-14 16:27:21.718 in main:i==2
//2012-06-14 16:27:21.828 in main:i==3
//2012-06-14 16:27:21.937 in main:i==4
//2012-06-14 16:27:22.046 in main:i==5
//2012-06-14 16:27:22.156 in main:i==6
//2012-06-14 16:27:22.265 in main:i==7
//2012-06-14 16:27:22.375 in main:i==8
//2012-06-14 16:27:22.484 in main:i==9
//2012-06-14 16:27:22.500 in testThread 1:i==1s
//2012-06-14 16:27:22.593 in main:i==10
//2012-06-14 16:27:22.703 in main:i==11
//2012-06-14 16:27:22.812 in main:i==12
//2012-06-14 16:27:22.921 in main:i==13
//2012-06-14 16:27:23.031 in main:i==14
//2012-06-14 16:27:23.140 in main:i==15
//2012-06-14 16:27:23.250 in main:i==16
//2012-06-14 16:27:23.359 in main:i==17
//2012-06-14 16:27:23.468 in main:i==18
//2012-06-14 16:27:23.500 in testThread 1:i==2s
//2012-06-14 16:27:23.578 in main:i==19
//2012-06-14 16:27:23.687 in main:i==20
//2012-06-14 16:27:23.796 in main:i==21
//2012-06-14 16:27:23.906 in main:i==22
//2012-06-14 16:27:24.015 in main:i==23
//2012-06-14 16:27:24.125 in main:i==24
//2012-06-14 16:27:24.234 in main:i==25
//2012-06-14 16:27:24.343 in main:i==26
//2012-06-14 16:27:24.453 in main:i==27
//2012-06-14 16:27:24.500 in testThread 1:i==3s
//2012-06-14 16:27:24.562 in main:i==28
//2012-06-14 16:27:24.671 in main:i==29
//2012-06-14 16:27:24.781 in main:i==30
//2012-06-14 16:27:24.890 in main:i==31
//2012-06-14 16:27:25.000 in main:i==32
//2012-06-14 16:27:25.109 in main:i==33
//2012-06-14 16:27:25.218 in main:i==34
//2012-06-14 16:27:25.328 in main:i==35
//2012-06-14 16:27:25.437 in main:i==36
//2012-06-14 16:27:25.500 in testThread 1:i==4s
//2012-06-14 16:27:25.546 in main:i==37
//2012-06-14 16:27:25.656 in main:i==38
//2012-06-14 16:27:25.765 in main:i==39
//2012-06-14 16:27:25.875 in main:i==40
//2012-06-14 16:27:25.984 in main:i==41
//2012-06-14 16:27:26.093 in main:i==42
//2012-06-14 16:27:26.203 in main:i==43
//2012-06-14 16:27:26.312 in main:i==44
//2012-06-14 16:27:26.421 in main:i==45
//2012-06-14 16:27:26.500 in testThread 1:i==5s
//2012-06-14 16:27:26.531 in main:i==46
//2012-06-14 16:27:26.531 =========END====================
paschen 版主 2016-03-11
  • 打赏
  • 举报
回复
定时器不会新开一个线程,是通过windows消息实现,系统按间隔给程序发送消息,而底层应该是通过中断实现
苦逼码农 2016-03-11
  • 打赏
  • 举报
回复
线程掉windows的调度单位, 定时器是WM_TIMER一种消息 windows有有消息队列,分系统消息队列,线程消息队列。 主线程创建了窗口,主线程维护一个消息队列。 工作线程一般是通过发送消息给主线程,让主线程去操作界面,理由是—————— mfc 的窗口指针不能跨线程!!!! 一旦跨线程就崩溃。 那么不用mfc,直接用win32 api写界面,可以操作界面否? 可以!!!!
fefe82 2016-03-11
  • 打赏
  • 举报
回复
线程和定时器完全不是一回事啊 ...
加载更多回复(6)

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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