0

我正在编写简单的日志框架,用户可以逐行或批量记录。User Write(message) 在他的代码和基于 bool flushEveryLine (每行为 true,批量写入为 false),通过调用 flush() 方法写入日志。消息在主线程上创建,当写入文件时,outputConsole 或 DebugConsole 使用 Task 在不同的线程上。List 是我想使用 TPL 在主线程和工作线程之间同步的内容。所以当 TimerEvent 发生时,丢失的消息应该被写入输出。问题是消息更改请参阅下面的消息。

    protected virtual void Write(LogMessage message)
    {
        if (!this.IsEnabled) // log enabled or disabled.
            return;
        List<Task> list = new List<Task>();
        foreach (Task task in _taskList)
        {
            if (task.Status != TaskStatus.RanToCompletion)
                list.Add(task);
        }
        _taskList = list;
        Task.WaitAll(_taskList.ToArray());
       lock(storage)
        {
            //Add message to messages
           **this.messages.Add(message);**

            if (FlushEveryLine)
                **Flush();**
        } 

    public void Flush()
    {

        if (messages.Count == 0)
            return;
        _taskToMessagesMap.Add(counter, this.messages);
       **Task.Factory.StartNew(() => FlushData(counter));**
        ++counter;
        if (counter > 30000) // reset counter after large increment.
        {
            counter = 0;
        }
        this.messages.Clear();// clear the messages to record new set of messages before writing.
    }

    protected virtual void FlushData(int index)
    {
        **List<LogMessage> messages = _taskToMessagesMap[index];**
        if (!IsEnabled) // logs enabled or not.
        {
            return;
        }
        lock (messages)
        {
            if (messages.Count == 0)
            {
                return;
            }
            IEnumerable<LogMessage> temp = messages.OrderBy(msg => msg.TimeStamp).ToArray();
             this.flush(temp);

        }
        return;

    }

代码说明::_taskToMessagesMap = new Dictionary(); // 存储计数器和消息列表的字典。这是 hack,因为我想测试 List 的行为。

Task.Factory.StartNew(() => FlushData(counter)); //实际上我想为每个线程使用 Flush(messages) 以便在调用 FlushData() 时应该使用传递的消息。但是我在执行工作线程时看到它使用当前消息列表。我知道列表是通过引用传递的,因此看到了这种行为,但我在上面的例子中也看到了整数的这种行为。示例:我的字典只有一个键 {0} 和所有测试消息,但是当它进入工作线程时,它会查找键 1。为什么?整数参数应按值传递。创建工作线程时,我将 0 作为参数传递。我怎样才能解决这个问题。

请帮忙,因为我被卡住了。

4

1 回答 1

2

闭包关闭变量而不是值。新值被提取是因为在传递给的 lambda 之前FlushData递增正在执行的变量的指令被执行。counterStartNew

解决此问题的一种简单方法是对局部变量中的当前值进行快照。

int currentCounter = counter;
Task.Factory.StartNew(() => FlushData(currentCounter));
++counter;

更新

您需要为要处理的任务制作数据副本,以便其他线程不会干扰写入消息。我会重写 Flush 和 FlushData 看起来更像这样:

public void Flush()
{
    if (messages.Count == 0)
        return;

    // make a copy of the list.
    var currentMessages = messages.ToList();

    Task.Factory.StartNew(() => FlushData(currentMessages));
    messages.Clear();// clear the messages to record new set of messages before writing.
}

protected virtual void FlushData(IEnumerable<LogMessage> toBeFlushed)
{
    if (!IsEnabled) // logs enabled or not.
    {
        return;
    }

    IEnumerable<LogMessage> temp = toBeFlushed.OrderBy(msg => msg.TimeStamp).ToArray();
    this.flush(temp);
}

Flush可能也应该锁定,storage因为它是一个可以从任何地方调用的公共方法。目前,消息可能会丢失。

于 2013-02-11T05:43:29.960 回答