首页 \ 问答 \ 记录线程的奇怪行为(Strange behaviour of logging thread)

记录线程的奇怪行为(Strange behaviour of logging thread)

我创建了以下程序来创建日志记录线程,并允许其他线程写入由此线程管理的共享缓冲区。 奇怪的是,如果我在stopLog函数中不包含对日志的写入,则日志永远不会关闭,并且线程不会终止。

#include "log.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define BUFFER_SIZE 128

static pthread_mutex_t logFileMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t logInUse = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t emptyCond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t fullCond = PTHREAD_COND_INITIALIZER;
static int bufferIsEmpty;
static int bufferIsFull;
static char buffer[BUFFER_SIZE];
static int in;
static int out;
static int count;
static int stop;

void * logOutput(void * in) {
    stop = 0;
    bufferIsEmpty = 1;
    bufferIsFull = 0;
    char ch;
    FILE * f = fopen((char *)in, "a");
    if (NULL != f) {
        fprintf(f, "---------------- Log Opened ----------------\n");
        while (!stop) {
            pthread_mutex_lock(&logFileMutex);
            while (bufferIsEmpty)
                pthread_cond_wait(&emptyCond, &logFileMutex);
            ch = buffer[out++];
            out %= BUFFER_SIZE;
            if (--count == 0)
                bufferIsEmpty = 1;
            bufferIsFull = 0;
            fputc(ch, f);
            pthread_cond_signal(&fullCond);
            pthread_mutex_unlock(&logFileMutex);
        }
        fprintf(f, "---------------- Log Closed ----------------\n");
        fflush(f);
        fclose(f);
    } else {
        fprintf(stderr, "Error opening log file %s\n", (char *)in);
    }
    pthread_exit(NULL);
}

void writeToLog(char * str) {
    pthread_mutex_lock(&logInUse);
    for (int i = 0; i <= strlen(str); i++) {
        pthread_mutex_lock(&logFileMutex);
        while (bufferIsFull)
            pthread_cond_wait(&fullCond, &logFileMutex);
        if (strlen(str) == i)
            buffer[in++] = '\n';
        else
            buffer[in++] = str[i];
        in %= BUFFER_SIZE;
        if (++count == (BUFFER_SIZE - 1))
            bufferIsFull = 1;
        bufferIsEmpty = 0;
        usleep(10);
        pthread_cond_signal(&emptyCond);
        pthread_mutex_unlock(&logFileMutex);
    }
    pthread_mutex_unlock(&logInUse);
}

void stopLog() {
    writeToLog("Stopping log...");
    stop = 1;
}

I have created the following program to create a logging thread and allow other threads to write into a shared buffer managed by this thread. Strangely though, if I do not include a write to the log in my stopLog function, the log is never closed and the thread does not terminate.

#include "log.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define BUFFER_SIZE 128

static pthread_mutex_t logFileMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t logInUse = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t emptyCond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t fullCond = PTHREAD_COND_INITIALIZER;
static int bufferIsEmpty;
static int bufferIsFull;
static char buffer[BUFFER_SIZE];
static int in;
static int out;
static int count;
static int stop;

void * logOutput(void * in) {
    stop = 0;
    bufferIsEmpty = 1;
    bufferIsFull = 0;
    char ch;
    FILE * f = fopen((char *)in, "a");
    if (NULL != f) {
        fprintf(f, "---------------- Log Opened ----------------\n");
        while (!stop) {
            pthread_mutex_lock(&logFileMutex);
            while (bufferIsEmpty)
                pthread_cond_wait(&emptyCond, &logFileMutex);
            ch = buffer[out++];
            out %= BUFFER_SIZE;
            if (--count == 0)
                bufferIsEmpty = 1;
            bufferIsFull = 0;
            fputc(ch, f);
            pthread_cond_signal(&fullCond);
            pthread_mutex_unlock(&logFileMutex);
        }
        fprintf(f, "---------------- Log Closed ----------------\n");
        fflush(f);
        fclose(f);
    } else {
        fprintf(stderr, "Error opening log file %s\n", (char *)in);
    }
    pthread_exit(NULL);
}

void writeToLog(char * str) {
    pthread_mutex_lock(&logInUse);
    for (int i = 0; i <= strlen(str); i++) {
        pthread_mutex_lock(&logFileMutex);
        while (bufferIsFull)
            pthread_cond_wait(&fullCond, &logFileMutex);
        if (strlen(str) == i)
            buffer[in++] = '\n';
        else
            buffer[in++] = str[i];
        in %= BUFFER_SIZE;
        if (++count == (BUFFER_SIZE - 1))
            bufferIsFull = 1;
        bufferIsEmpty = 0;
        usleep(10);
        pthread_cond_signal(&emptyCond);
        pthread_mutex_unlock(&logFileMutex);
    }
    pthread_mutex_unlock(&logInUse);
}

void stopLog() {
    writeToLog("Stopping log...");
    stop = 1;
}

原文:https://stackoverflow.com/questions/36122192
更新时间:2019-10-16 04:38

最满意答案

如果缓冲区为空并且在pthread_cond_wait()上阻止了logOutput() ,则设置stop = 1不会将其唤醒。 它会在下次唤醒时才会注意到,并且写入一些日志输出可以实现这一点。

作为线程之间共享的变量,还需要保护stop免受不同步的访问。

您可以通过将logOutput()的循环更改为来解决这两个问题

pthread_mutex_lock(&logFileMutex);
while (!stop)
{
    while (!stop && bufferIsEmpty)
        pthread_cond_wait(&emptyCond, &logFileMutex);

    while (!bufferIsEmpty)
    {
        ch = buffer[out++];
        out %= BUFFER_SIZE;
        if (--count == 0)
            bufferIsEmpty = 1;
        fputc(ch, f);
    }
    bufferIsFull = 0;
    pthread_cond_signal(&fullCond);
}
pthread_mutex_unlock(&logFileMutex);

并将stopLog()更改为:

void stopLog() {
    pthread_mutex_lock(&logFileMutex);
    stop = 1;
    pthread_cond_signal(&emptyCond);
    pthread_mutex_unlock(&logFileMutex);
}

现在,当stopLog()时, logOutput()将被唤醒,处理在停止和退出的同时发送的任何日志消息。


If the buffer is empty and logOutput() is blocked on pthread_cond_wait(), setting stop = 1 is not going to wake it up. It won't notice until the next time it wakes, and writing some log output accomplishes that.

As a variable shared between threads, stop also needs to be protected from unsynchronised accesses.

You can fix both these problems by changing the loop in logOutput() to

pthread_mutex_lock(&logFileMutex);
while (!stop)
{
    while (!stop && bufferIsEmpty)
        pthread_cond_wait(&emptyCond, &logFileMutex);

    while (!bufferIsEmpty)
    {
        ch = buffer[out++];
        out %= BUFFER_SIZE;
        if (--count == 0)
            bufferIsEmpty = 1;
        fputc(ch, f);
    }
    bufferIsFull = 0;
    pthread_cond_signal(&fullCond);
}
pthread_mutex_unlock(&logFileMutex);

and changing stopLog() to:

void stopLog() {
    pthread_mutex_lock(&logFileMutex);
    stop = 1;
    pthread_cond_signal(&emptyCond);
    pthread_mutex_unlock(&logFileMutex);
}

Now, when stopLog() is called, logOutput() will wake up, process any log messages that were sent at the same time as the stop and exit.

2016-03-21

相关问答

更多

为什么C#编译器在从不同的基类派生时抱怨“类型可能统一”?(Why does the C# compiler complain that “types may unify” when they derive from different base classes?)

这是C#4规范的第13.4.2节的结果,其中规定: 如果从C创建的任何可能的构造类型将在类型参数被替换为L之后,导致L中的两个接口相同,则C的声明无效。 确定所有可能的构造类型时,不考虑约束声明。 请注意第二句话。 因此编译器不是bug; 编译器是正确的。 人们可能会认为这是语言规范的缺陷。 一般来说,几乎每种情况都会忽略约束,在这种情况下,必须推测一个通用类型的事实。 约束主要用于确定通用类型参数的有效基类 ,而其他一些参数。 不幸的是,有时会导致语言不必要地严格的情况,正如您所发现的那样。

为什么Eclipse Juno 4.2运行“JPA Java Change Event Handler”进程?(Why is Eclipse Juno 4.2 running “JPA Java Change Event Handler” processes?)

不幸的是,JPA java完成提议扩展点不正确地强制oejpt.jpa.core插件激活。 一旦我们的插件被激活,我们开始监听java事件和facet事件,无论JPA是否刻面。 在Juno中,这些后台线程被转换为Eclipse Jobs框架。 在Helios中,您不会在Progress视图中看到它们,但存在同样的问题。 感谢您为此问题输入了对大理https://bugs.eclipse.org/bugs/show_bug.cgi?id=386171的错误。 更新 :这个bug已经修复了Juno

内核堆栈和用户空间堆栈(kernel stack and user space stack)

内核堆栈和用户堆栈有什么区别? 总而言之,除了在内存中使用不同的位置(因此对于stackpointer寄存器使用不同的值),通常还有不同的内存访问保护。 即,在用户模式下执行时,内核内存(其中一部分是内核堆栈)即使被映射也将无法访问。 反之亦然,内核代码没有明确的请求(在Linux中,通过诸如copy_from_user()功能),用户内存(包括用户堆栈)通常不能直接访问。 为什么使用[单独]内核堆栈? 分离特权和安全。 一个,用户空间程序可以使他们的堆栈(指针)任何他们想要的东西,通常没有架构

ElasticSearch - 返回唯一值(ElasticSearch - Return Unique Values)

您可以使用术语聚合 。 { "size": 0, "aggs" : { "langs" : { "terms" : { "field" : "language", "size" : 0 } } }} 搜索将返回以下内容: { "took" : 16, "timed_out" : false, "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "hits" : { "total"

C#:等待所有线程完成(C#: Waiting for all threads to complete)

我仍然认为使用Join更简单。 记录预期的完成时间(如现在+超时),然后在一个循环中 if(!thread.Join(End-now)) throw new NotFinishedInTime(); I still think using Join is simpler. Record the expected completion time (as Now+timeout), then, in a loop, do if(!thread.Join(End-now)) thro

什么时候App Engine调度程序使用新线程与新实例?(When does the App Engine scheduler use a new thread vs. a new instance?)

当前使用以下一组规则来确定给定的实例是否可以接受新的请求: if processing more than N concurrent requests (today N=10): false elif exceeding the soft memory limit: false elif exceeding the instance class CPU limit: false elif warming up: false else true 以下CPU /核心限制总共适用于每个实例类: CL

使用RecyclerView和CardView触摸反馈(Touch feedback with RecyclerView and CardView)

假设您正在使用Material / Appcompat主题和Lollipop,我可以使CardView具有以下属性: android:focusable="true" android:clickable="true" android:foreground="?android:attr/selectableItemBackground" I was now able to solve the issue. The problem is that i use onClickListener on

无法使用install.packages安装R-forge软件包(Cannot install R-forge package using install.packages)

由于几个不同的原因,R-Forge可能无法构建包。 可能的是,文档尚未更新,以反映代码中最近的更改。 或者,可能是一些依赖关系在构建时不可用。 您可以使用svn检出源代码。 首先,在R-Forge网站上搜索该项目,并转到项目主页 - 例如http://r-forge.r-project.org/projects/returnanalytics/点击SCM链接进入页面像这样http://r-forge.r-project.org/scm/?group_id=579 此页面将告诉您用于签出项目的命令

相关文章

更多

最新问答

更多
  • 在svg - gstatic urls中嵌入'Open Sans'字体(Embedding 'Open Sans' font in svg - gstatic urls)
  • 停止python线程等待popen?(stopping a python thread waiting for popen?)
  • Julia:如何从文本文件导入图形(csv具有不等数量的'列')?(Julia: How to import a graph from text file (csv with unequal number of 'columns')?)
  • 在另一个插件中使用qgis核心插件(Useage of qgis core plugin in another plugin)
  • jQuery .get缓存工作得太好了?(jQuery .get caching working too well?)
  • Gameplay3D gameplay :: TimeListener(Gameplay3D gameplay::TimeListener)
  • 获取ItemDataBound中的行数(Get row count in ItemDataBound)
  • NHibernate SchemaExport(config).Create(false,true)不会使用SQL Server 2014 LocalDB创建数据库架构(NHibernate SchemaExport(config).Create(false, true) does not create DB Schema with SQL Server 2014 LocalDB)
  • 列出迭代器失效(List iterator disfunctionality)
  • 列出多个变量的理解(List comprehension with multiple variables)
  • 如何在asp.net MVC5中显示锁定结束时间(How to Display locking end time in asp.net MVC5)
  • 在android中的ImageView上显示TextView(Display TextView over ImageView in android)
  • 64位sharemem.pas / borlndmm.dll XE2(64-bit sharemem.pas/borlndmm.dll XE2)
  • 为什么分离和追加比直接更改html更有效?(Why is detaching and appending more efficient than changing html directly?)
  • Eclipse将“排序成员”设置保存到repo(Eclipse Saving “Sort members” settings to repo)
  • 当我查看最近标记的instagram API时,为什么帖子似乎会改变(why do the posts seem to change when i look at the recent tagged instagram API)
  • 使用php邮件功能发送电子邮件 - 变慢[关闭](Sending email using php mail function - Getting slow [closed])
  • Web服务器可以写入登录用户的屏幕吗?(Can a web server write to a logged-in user's screen?)
  • CFNetwork匿名密码?(CFNetwork Anonymous Ciphers?)
  • 更快的算法来计算一个范围内的特定整数可以分割多少个数字(Faster algorithm to count how many numbers are divisible by a specific integer in a range)
  • 用于散点图的Matplotlib FuncAnimation(Matplotlib FuncAnimation for scatter plot)
  • Gradle:Manifest merge更改AndroidManifest文件中“theme”参数的值:(Gradle: Manifest merge changes the value for the “theme” parameters in the AndroidManifest file:)
  • 如何在属性中渲染cms / static块(How do I render cms/static blocks in an attribute)
  • Javascript显示错误和页面重新加载问题(Javascript display error and page reload problems)
  • android官方固件签名(android official firmware signature)
  • 在这种情况下,有比eval()更好的方法吗?(Is there a better way than eval() in this scenario?)
  • primefaces commandbutton actionlistener未调用(primefaces commandbutton actionlistener not called)
  • C将指针传递给堆栈上的数组(C passing a pointer to an array on the stack)
  • 简单的产品内部可配置产品(Simple product inside configurable product)
  • nhibernate HbmMapping到Xml(nhibernate HbmMapping to Xml)