立即生效的可变时长定时器

简介: 立即生效的可变时长定时器

select 出错! errno:22(EINVAL)。

随时可以修改定时间隔并立刻生效的可变时长定时器。

众所周知NSTimer,在一个定时间隔之间,不能修改定时间隔。想修改定时间隔只有等定时器超时,重新关闭定时器,并修改时间间隔,并重新启动定时器才行。

那么若等到一个定时间隔结束,那么就需要等待很久,若你的定时间隔很短,你就需要定时器运行周期变短,那么你的应用耗电量就要提高,若运行间隔超短,那么你的耗电量就超快,并且时间精度也会下降,毕竟cpu不会总时实时(100毫秒以内)给定时器。下面我们介绍一个零延迟(指的是修改的定时间隔立刻生效)可变时长定时器。

通过BSD线程,管道,select来实现无延迟可变时长定时器。当然你也可以用NSThread线程代替BSD线程。

修改的定时器时长可以通过管道发送过来,关闭定时器也可以通过管道发送过来的字符串来决定。

定时器绝大部分时间停留在select函数处等待管道消息或超时,handleConnectAbort相当于定时器函数。

生成管道的代码:

-(void)initData
{
    FLDDLogDebug(@"函数");

    self.guardModifyWaitTimePipe = [NSPipe pipe];
    self.guardModifyWaitTimePipeWriteHandle = [self.guardModifyWaitTimePipe fileHandleForWriting];
    self.fdGuardModifyWaitTimeReadPipe = [[self.guardModifyWaitTimePipe fileHandleForReading] fileDescriptor];
}

调用处的代码:

            _connectTime = (long long)[[NSDate date] timeIntervalSince1970];
            FLDDLogDebug(@"_connectTime = %lld\n", _connectTime);
            [self.guardModifyWaitTimePipeWriteHandle writeData: [@"GuardThreadStartMonitor:10;" dataUsingEncoding: NSASCIIStringEncoding]];
            
            int i = connect(server_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
            FLDDLogDebug(@"connect after _connectTime = %lld, self.testGuardThread : %ld\n", (long long)[[NSDate date] timeIntervalSince1970], self.testGuardThread);

            _connectTime = 0;
            [self.guardModifyWaitTimePipeWriteHandle writeData: [@"GuardThreadIdle:1680;" dataUsingEncoding: NSASCIIStringEncoding]];

可变时长定时器BSD线程代码:

-(void)guardThread
{
    FLDDLogDebug(@"函数");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [[NSThread currentThread] setName:@"socket thread"];
        FLDDLogDebug(@"guard thread");
        
        //守护线程大循环
        while(1)
        {
            fd_set read_fd_set;
            FD_ZERO(&read_fd_set);
            FD_SET(self.fdGuardModifyWaitTimeReadPipe, &read_fd_set);
            struct timeval tv;
            
            tv.tv_sec = self.guardThreadWaitTimeInterval;
            tv.tv_usec = 0;
            
            //socket一般都处于可写状态,除了网卡满了写不进去的罕见情况,并且若时线程外发送的消息都写了管道消息,管道有数据可度也会结束侦听,所以该处侦听一般都是立刻响应
            long ret = select(self.fdGuardModifyWaitTimeReadPipe + 1, &read_fd_set, NULL, NULL, &tv);
            if(ret > 0)
            {
                //先把消息接收过来,后面再处理,防止再处理消息时,连接异常。
                if (FD_ISSET(self.fdGuardModifyWaitTimeReadPipe, &read_fd_set))
                {
                    FLDDLogDebug(@"ret = %ld\n", ret);
                    long nbytes;
                    unsigned char readBuffer[PIPE_MESSAGE_MAX_BUFFER_LEN] = {0};
                    nbytes = read(self.fdGuardModifyWaitTimeReadPipe, readBuffer, PIPE_MESSAGE_MAX_BUFFER_LEN);
                    if (nbytes <= 0) {
                        FLDDLogDebug(@"no data.");
                    }
                    else
                    {
                        //防止收到的管道消息粘包,超过最大缓冲区长度,最后一个缓冲区字节强制置为结束符号
                        readBuffer[PIPE_MESSAGE_MAX_BUFFER_LEN - 1] = '\n';
                        NSString *str = [[NSString alloc] initWithString:[NSString stringWithFormat:@"%s", readBuffer]];
                        FLDDLogDebug(@"pipe data:%@", str);
                        NSRange rangeGuardThreadIdle=[str rangeOfString:@"GuardThreadIdle:" options:NSBackwardsSearch];
                        NSRange rangeGuardThreadStartMonitor=[str rangeOfString:@"GuardThreadStartMonitor:" options:NSBackwardsSearch];
                        
                        if(rangeGuardThreadIdle.length > 0)
                        {
                            FLDDLogDebug(@"收到守护线程空闲的消息,停止实时监控长连接线程:%@", str);
//                            self.guardThreadWaitTimeInterval = GUARD_THREAD_MAX_WAIT_TIME_INTERVAL;
                            NSRange range1 =[str rangeOfString:@";" options:NSBackwardsSearch];
                            if((range1.length > 0) && (range1.location > rangeGuardThreadIdle.length + rangeGuardThreadIdle.location))
                            {
                                NSRange range2 = NSMakeRange (rangeGuardThreadIdle.location + rangeGuardThreadIdle.length, range1.location - rangeGuardThreadIdle.location - rangeGuardThreadIdle.length);
                                NSString *number = [str substringWithRange : range2];
                                if(number.length > 0)
                                {
                                    self.guardThreadWaitTimeInterval = [number longLongValue];
                                }
                                else
                                {
                                    self.guardThreadWaitTimeInterval = GUARD_THREAD_MAX_WAIT_TIME_INTERVAL;
                                }
                            }

                        }
                        else if(rangeGuardThreadStartMonitor.length > 0)
                        {
                            FLDDLogDebug(@"收到守护线程开始监控的消息,开始实时监控长连接线程:%@", str);
//                            //如果已经连接Xcode调试不再处理connect异常的问题
//                            if(isatty(STDOUT_FILENO)) {
//                                return;
//                            }
//                            self.guardThreadWaitTimeInterval = GUARD_THREAD_MIN_WAIT_TIME_INTERVAL;
                            NSRange range1 =[str rangeOfString:@";" options:NSBackwardsSearch];
                            if((range1.length > 0) && (range1.location > rangeGuardThreadStartMonitor.length + rangeGuardThreadStartMonitor.location))
                            {
                                NSRange range2 = NSMakeRange (rangeGuardThreadStartMonitor.location + rangeGuardThreadStartMonitor.length, range1.location - rangeGuardThreadStartMonitor.location - rangeGuardThreadStartMonitor.length);
                                NSString *number = [str substringWithRange : range2];
                                if(number.length > 0)
                                {
                                    self.guardThreadWaitTimeInterval = [number longLongValue];
                                }
                                else
                                {
                                    self.guardThreadWaitTimeInterval = GUARD_THREAD_MIN_WAIT_TIME_INTERVAL;
                                }
                            }
                        }
                    }
                    
                }
                
                
            }
            else if(ret ==0){
                FLDDLogDebug(@"select 超时!\n");
                [self handleConnectAbort];
            }
            else
            {
                FLDDLogDebug(@"select 出错!\n");
            }
        }

    });
}

至于启动定时器BSD线程的代码我就不用写了吧?自己在自己需要的情况下,直接调用[self guardThread];也可以在应用起来时直接调用,或发通知调用。你的地盘你做主。这个在后台运行的线程我搞了很久才找到。有的技术就是和你隔一张纸,实际上很简单,但是问题就在你不知道。不过它只能在后台存活三分钟。

下面是一个测试GCD线程使用MainDispatchQueue证明它在后台能正常运行的代码,做一个单例或页面,在代码中调用test函数,可以看到,它在后台还照常打日志三分钟,这种起线程的方式独占cpu只能刷新一次UI:

-(void)test
{
    dispatch_async(dispatch_get_main_queue(), ^{
        for(NSInteger i = 0; ; i++)
        {
            sleep(1);
            NSLog(@"test:%ld", (long)i);
        }
    });
}
目录
相关文章
|
2月前
定时器 在某个时间到达之后,执行指定的任务
本文介绍了两种实现定时器的方法:基于优先级队列(堆)和基于时间轮,以在指定时间到达后执行特定任务。
38 0
定时器 在某个时间到达之后,执行指定的任务
|
6月前
|
Java
java线程池执行任务(一次任务、固定间隔时间任务等)
java线程池执行任务(一次任务、固定间隔时间任务等)
327 1
|
6月前
|
C#
C# 版本的 计时器类 精确到微秒 秒后保留一位小数 支持年月日时分秒带单位的输出
这篇2010年的文章是从别处搬运过来的,主要包含一个C#类`TimeCount`,该类有多个方法用于处理时间相关的计算。例如,`GetMaxYearCount`计算以毫秒为单位的最大年数,`GetCurrentTimeByMiliSec`将当前时间转换为毫秒,还有`SecondsToYYMMDDhhmmss`将秒数转换为年月日时分秒的字符串。此外,类中还包括一些辅助方法,如处理小数点后保留一位数字的`RemainOneFigureAfterDot`。
上下文本间隔自定义滚动时间的封装函数
上下文本间隔自定义滚动时间的封装函数
64 0
上下文本间隔自定义滚动时间的封装函数
如果需要手动写动画,你认为最小时间间隔是多久,为什么?
如果需要手动写动画,你认为最小时间间隔是多久,为什么?
67 0
|
前端开发
时间秒转换为毫秒
时间秒转换为毫秒
127 0
|
存储
TIM定时中断(内含:1.TIM简介+2.定时器类型+3.基本定时器+4.通用定时器+5.高级定时器+6.定时中断基本结构+7.预分频器时序+8.计数器时序+9.计数器有/无预装+10.RCC时钟树)
TIM定时中断(内含:1.TIM简介+2.定时器类型+3.基本定时器+4.通用定时器+5.高级定时器+6.定时中断基本结构+7.预分频器时序+8.计数器时序+9.计数器有/无预装+10.RCC时钟树)
501 0
TIM定时中断(内含:1.TIM简介+2.定时器类型+3.基本定时器+4.通用定时器+5.高级定时器+6.定时中断基本结构+7.预分频器时序+8.计数器时序+9.计数器有/无预装+10.RCC时钟树)