使用 GCD 在 HTML 文件中写入日志无法按预期工作

Write logs in HTML file using GCD doesn't work as expected

我为 NSLog 创建了一个重载方法,以便将所有日志写入 HTML 文件。

所有日志都使用 GCD.

写入 HTML 文件

问题是线路有时会 运行...

这是我的代码:

写入日志文件功能:

+(void)writeInLogFile:(NSString *)strLog inFolder:(NSString *)folder fileName:(NSString *)fileName extension:(NSString *)extension{
//Create Directory
NSString *path;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:folder];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:path])    //Does directory already exists?
{
    if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error])
    {
        NSLog(@"%d||Create log directory error: %@",LOG_SEVERITY_HIGH, error);
    }
}

NSString* filePath = [NSString stringWithFormat:@"%@/%@.%@",path,fileName,extension];


int nbOfLogFiles = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] count];


if(nbOfLogFiles <= NB_OF_LOG_FILES_BEFORE_PURGE +1){
    [HandleString createLogFile:filePath andStrLog:strLog];
}
    else{
        [HandleString purgeLogDirectory:path];
        [HandleString createLogFile:filePath andStrLog:strLog];
    }
}

这就是结果:

调用(每次执行 NSLog 时调用):

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    [HandleString writeInLogFile:message];
});

这就是结果:

如您所见,最后一行是 t运行cated...

这贯穿整个文件。

我尝试 运行 主线程上的进程,它运行良好,没有问题。

另一个有趣的事情是,当我更改 QOS 时结果不一样,例如优先级高,我有更多 t运行 线路。

编辑:写入文件的代码:

+(void)createLogFile:(NSString*)filePath andStrLog:(NSString*)strLog{
if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]){
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
}

    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
    [fileHandle seekToEndOfFile];
    [fileHandle writeData:[strLog dataUsingEncoding:NSUTF8StringEncoding]];
    [fileHandle closeFile];
}

考虑到短语 "I tried to run the process on the main thread and it works well without problems." 我会说您的截断问题来自缓冲区在完全清空之前被关闭。这并不能解释您在使用高优先级时可能观察到的差异(请记住,额外的行可能是巧合,除非另有证明......)。

现在,我建议您在结束线程作用域之前尝试一个小 sleep...

我找到了一个有点棘手的解决方案:

我删除了 dispatch_asynch :

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    [HandleString writeInLogFile:message];
});

在我写入日志文件的函数中,我做了:

static dispatch_queue_t asyncQueue;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
    asyncQueue = dispatch_queue_create("asyncQueue", NULL);
});

if(nbOfLogFiles <= NB_OF_LOG_FILES_BEFORE_PURGE +1){
    dispatch_async(asyncQueue, ^{
        [HandleString createLogFile:filePath andStrLog:strLog];
    });
}
else{
    dispatch_async(asyncQueue, ^{
        [HandleString purgeLogDirectory:path];
        [HandleString createLogFile:filePath andStrLog:strLog];
    });

}

可能会对某人有所帮助。

但我想知道您认为这是不是一个好的解决方案?

约翰

问题是您同时从多个线程写入。考虑以下事件序列:

  • 线程 A 寻找文件末尾
  • 线程 B 寻找文件末尾
  • 线程 B 写入数据
  • 线程A写入数据

线程 A 的数据将覆盖线程 B 的数据。如果Thread A的数据更长,那么Thread B的数据就不会留下任何痕迹。如果线程A的数据较短,则线程B的数据超出该长度的部分将保留。

调换最后两步的顺序也有类似的问题。当然,还有更复杂的线程更多的场景。

一种解决方案是序列化对文件的所有访问,就像您在自我回答中所做的那样。另一种是以仅追加模式打开文件。在这种模式下,所有写入都在文件末尾完成。这是由 OS 强制执行的。两个同时写入无法相互覆盖。文件句柄的当前文件位置是无关紧要的,所以不需要寻找。

NSFileHandle 不直接支持以仅追加模式打开文件。您必须使用 open() 打开一个文件描述符,然后将该文件描述符交给 NSFileHandle。 (你也可以只使用 write()close(),但它有点混乱。)例如:

int fd = open(filePath.fileSystemRepresentation, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0)
{
    // handle error
}
NSFileHandle* fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES];
[fileHandle writeData:[strLog dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
[fileHandle release]; // Only needed if not using ARC

请注意,在这种情况下您不必显式创建日志文件。由于 O_CREAT 标志,如果它不存在,open() 调用将创建它。