Как эффективно зарегистрироваться асинхронно?

Запустите msbuild Foo.sln /t:Rebuild /v:diag (из C:\Program Files (x86)\MSBuild\12.0\bin), чтобы построить свое решение из командной строки и получить более подробную информацию, затем найдите .csproj., который регистрирует предупреждение и проверяет его ссылки и ссылки других проектов, которые используют тот же общая сборка, которая отличается версией.

Редактирование. Вы также можете установить многословность сборки непосредственно в VS2013. Перейдите в меню Tools> Options, затем перейдите к Projects and Solutions и установите многословие MSBuild в Diagnostic.

Редактирование: немного разъяснений, поскольку я только что получил его. В моем случае предупреждение было связано с тем, что я добавлял ссылку, используя приглашение Resharper, а не диалог Add Reference, который сделал это бездействующим, хотя оба варианта v4 и v12 доступны для выбора.

<Reference Include="Microsoft.Build, Version=12.0.0.0, ..." />
<Reference Include="Microsoft.Build.Framework" />

vs

<Reference Include="Microsoft.Build, Version=12.0.0.0, ..." />
<Reference Include="Microsoft.Build.Framework, Version=12.0.0.0, ..." />

В журнале MSBuild с многообразием /v:diag это выглядело следующим образом. давая детали, с которыми столкнулись две ссылки: -

  There was a conflict between 
  "Microsoft.Build.Framework, Version=4.0.0.0, ..." and 
  "Microsoft.Build.Framework, Version=12.0.0.0, ...". (TaskId:16)

      "Microsoft.Build.Framework, Version=4.0.0.0, ..." was chosen because it was primary and 
      "Microsoft.Build.Framework, Version=12.0.0.0, ..." was not. (TaskId:16)

      References which depend on "Microsoft.Build.Framework, Version=4.0.0.0, ..." 
      [C:\...\v4.5.1\Microsoft.Build.Framework.dll]. (TaskId:16)

          C:\...\v4.5.1\Microsoft.Build.Framework.dll (TaskId:16)
            Project file item includes which caused reference "C:\...\v4.5.1\Microsoft.Build.Framework.dll". (TaskId:16)
              Microsoft.Build.Framework (TaskId:16)

      References which depend on "Microsoft.Build.Framework, Version=12.0.0.0, ..." 
      [C:\...\v12.0\Microsoft.Build.Framework.dll]. (TaskId:16)

          C:\...\v12.0\Microsoft.Build.dll (TaskId:16)
            Project file item includes which caused reference "C:\...\v12.0\Microsoft.Build.dll". (TaskId:16)
              Microsoft.Build, Version=12.0.0.0, ... (TaskId:16)

          C:\...\v12.0\Microsoft.Build.Engine.dll (TaskId:16)
            Project file item includes which caused reference "C:\...\v12.0\Microsoft.Build.Engine.dll". (TaskId:16)
              Microsoft.Build, Version=12.0.0.0, ... (TaskId:16)

C:\Program Files (x86)\MSBuild\12.0\bin\Microsoft.Common.CurrentVersion.targets(1697,5): warning MSB3277: 
Found conflicts between different versions of the same dependent assembly that could not be resolved.  
These reference conflicts are listed in the build log when log verbosity is set to detailed. 
[C:\Users\Ilya.Kozhevnikov\Dropbox\BuildTree\BuildTree\BuildTree.csproj]
30
задан Eric Schoonover 25 July 2009 в 08:11
поделиться

8 ответов

Я написал этот код некоторое время назад, не стесняйтесь использовать его.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MediaBrowser.Library.Logging {
    public abstract class ThreadedLogger : LoggerBase {

        Queue<Action> queue = new Queue<Action>();
        AutoResetEvent hasNewItems = new AutoResetEvent(false);
        volatile bool waiting = false;

        public ThreadedLogger() : base() {
            Thread loggingThread = new Thread(new ThreadStart(ProcessQueue));
            loggingThread.IsBackground = true;
            loggingThread.Start();
        }


        void ProcessQueue() {
            while (true) {
                waiting = true;
                hasNewItems.WaitOne(10000,true);
                waiting = false;

                Queue<Action> queueCopy;
                lock (queue) {
                    queueCopy = new Queue<Action>(queue);
                    queue.Clear();
                }

                foreach (var log in queueCopy) {
                    log();
                }
            }
        }

        public override void LogMessage(LogRow row) {
            lock (queue) {
                queue.Enqueue(() => AsyncLogMessage(row));
            }
            hasNewItems.Set();
        }

        protected abstract void AsyncLogMessage(LogRow row);


        public override void Flush() {
            while (!waiting) {
                Thread.Sleep(1);
            }
        }
    }
}

Некоторые преимущества:

  • Он поддерживает работу фонового регистратора, поэтому ему не нужно раскручивать и раскручивать потоки.
  • Он использует один поток для обслуживания очереди, что означает, что никогда не будет ситуации, когда 100 потоков обслуживают очередь.
  • Копирует очереди, чтобы гарантировать, что очередь не заблокирована во время выполнения операции регистрации.
  • Он использует AutoResetEvent, чтобы гарантировать, что поток bg находится в состоянии ожидания
  • . ИМХО, им очень легко следовать

Вот немного улучшенная версия, имейте в виду, что я провел очень небольшое тестирование, но она решает некоторые незначительные проблемы.

public abstract class ThreadedLogger : IDisposable {

    Queue<Action> queue = new Queue<Action>();
    ManualResetEvent hasNewItems = new ManualResetEvent(false);
    ManualResetEvent terminate = new ManualResetEvent(false);
    ManualResetEvent waiting = new ManualResetEvent(false);

    Thread loggingThread; 

    public ThreadedLogger() {
        loggingThread = new Thread(new ThreadStart(ProcessQueue));
        loggingThread.IsBackground = true;
        // this is performed from a bg thread, to ensure the queue is serviced from a single thread
        loggingThread.Start();
    }


    void ProcessQueue() {
        while (true) {
            waiting.Set();
            int i = ManualResetEvent.WaitAny(new WaitHandle[] { hasNewItems, terminate });
            // terminate was signaled 
            if (i == 1) return; 
            hasNewItems.Reset();
            waiting.Reset();

            Queue<Action> queueCopy;
            lock (queue) {
                queueCopy = new Queue<Action>(queue);
                queue.Clear();
            }

            foreach (var log in queueCopy) {
                log();
            }    
        }
    }

    public void LogMessage(LogRow row) {
        lock (queue) {
            queue.Enqueue(() => AsyncLogMessage(row));
        }
        hasNewItems.Set();
    }

    protected abstract void AsyncLogMessage(LogRow row);


    public void Flush() {
        waiting.WaitOne();
    }


    public void Dispose() {
        terminate.Set();
        loggingThread.Join();
    }
}

Преимущества перед оригиналом:

  • Он одноразовый, поэтому вы можете избавиться от асинхронного регистратора
  • Улучшена семантика сброса
  • Он будет немного лучше реагировать на взрыв, сопровождаемый тишиной
34
ответ дан Sam Saffron 11 October 2019 в 13:41
поделиться

Да, вам нужна очередь производителя / потребителя. У меня есть один пример этого в моем учебнике по потокам - если вы посмотрите мою страницу "deadlocks / monitor методов" , вы найдете код во второй половине.

Конечно, в сети есть и много других примеров - и .NET 4.0 будет поставляться с одним в фреймворке (скорее, более полнофункциональным, чем мой!). В .NET 4.0 вы, вероятно, включили бы ConcurrentQueue<T> в BlockingCollection<T> .

Версия на этой странице не является общей (она была написана давно назад), но вы, вероятно, захотите сделать ее общей - это будет тривиально.

Вы должны вызывать Produce из каждого «нормального» потока и Consume из одного потока, просто зацикливаясь и регистрируя все, что он потребляет. Вероятно, проще всего сделать потребительский поток фоновым потоком, поэтому вам не нужно беспокоиться об «остановке» очереди при выходе из приложения. Это означает, что есть удаленная возможность пропустить окончательную запись журнала (если она наполовину записывается при выходе из приложения) - или даже больше, если вы производите быстрее, чем оно может потреблять / log.

11
ответ дан Jon Skeet 11 October 2019 в 13:41
поделиться

Я хотел сказать, что мой предыдущий пост был бесполезным. Вы можете просто установить AutoFlush на true, и вам не придется перебирать всех слушателей. Тем не менее, у меня все еще была сумасшедшая проблема с параллельными потоками, пытающимися очистить регистратор. Мне пришлось создать еще одно логическое значение, которое было установлено в значение true во время копирования очереди и выполнения записей LogEntry, а затем в процедуре сброса мне пришлось проверить это логическое значение, чтобы убедиться, что чего-то еще не было в очереди и что ничего не обрабатывалось. перед возвращением.

Теперь несколько параллельных потоков могут поразить эту вещь, и когда я вызываю flush, я знаю, что это действительно flush.

     public static void FlushLogs()
    {
        int queueCount;
        bool isProcessingLogs;
        while (true)
        {
            //wait for the current iteration to complete
            m_waitingThreadEvent.WaitOne();

            //check to see if we are currently processing logs
            lock (m_isProcessingLogsSync)
            {
                isProcessingLogs = m_isProcessingLogs;
            }

            //check to see if more events were added while the logger was processing the last batch
            lock (m_loggerQueueSync)
            {
                queueCount = m_loggerQueue.Count;
            }                

            if (queueCount == 0 && !isProcessingLogs)
                break;

            //since something is in the queue, reset the signal so we will not keep looping

            Thread.Sleep(400);
        }
    }
1
ответ дан Johnny Serrano 11 October 2019 в 13:41
поделиться

Если вы имеете в виду очередь SHARED, то я думаю, что вам придется синхронизировать записи в нее, нажатия и всплывающие окна.

Но я все еще думаю, что стоит стремиться к дизайну общей очереди. По сравнению с IO ведения журнала и, вероятно, по сравнению с другой работой, выполняемой вашим приложением, краткая блокировка толчков и всплывающих окон, вероятно, не будет существенной.

0
ответ дан Corey Trager 11 October 2019 в 13:41
поделиться

Вот то, что я придумал ... также см. Ответ Сэма Шафрана. Этот ответ - вики сообщества, если есть какие-то проблемы, которые люди видят в коде и хотят обновить.

/// <summary>
/// A singleton queue that manages writing log entries to the different logging sources (Enterprise Library Logging) off the executing thread.
/// This queue ensures that log entries are written in the order that they were executed and that logging is only utilizing one thread (backgroundworker) at any given time.
/// </summary>
public class AsyncLoggerQueue
{
    //create singleton instance of logger queue
    public static AsyncLoggerQueue Current = new AsyncLoggerQueue();

    private static readonly object logEntryQueueLock = new object();

    private Queue<LogEntry> _LogEntryQueue = new Queue<LogEntry>();
    private BackgroundWorker _Logger = new BackgroundWorker();

    private AsyncLoggerQueue()
    {
        //configure background worker
        _Logger.WorkerSupportsCancellation = false;
        _Logger.DoWork += new DoWorkEventHandler(_Logger_DoWork);
    }

    public void Enqueue(LogEntry le)
    {
        //lock during write
        lock (logEntryQueueLock)
        {
            _LogEntryQueue.Enqueue(le);

            //while locked check to see if the BW is running, if not start it
            if (!_Logger.IsBusy)
                _Logger.RunWorkerAsync();
        }
    }

    private void _Logger_DoWork(object sender, DoWorkEventArgs e)
    {
        while (true)
        {
            LogEntry le = null;

            bool skipEmptyCheck = false;
            lock (logEntryQueueLock)
            {
                if (_LogEntryQueue.Count <= 0) //if queue is empty than BW is done
                    return;
                else if (_LogEntryQueue.Count > 1) //if greater than 1 we can skip checking to see if anything has been enqueued during the logging operation
                    skipEmptyCheck = true;

                //dequeue the LogEntry that will be written to the log
                le = _LogEntryQueue.Dequeue();
            }

            //pass LogEntry to Enterprise Library
            Logger.Write(le);

            if (skipEmptyCheck) //if LogEntryQueue.Count was > 1 before we wrote the last LogEntry we know to continue without double checking
            {
                lock (logEntryQueueLock)
                {
                    if (_LogEntryQueue.Count <= 0) //if queue is still empty than BW is done
                        return;
                }
            }
        }
    }
}
2
ответ дан 3 revs 11 October 2019 в 13:41
поделиться

Если вы регистрируете что-то в отдельном потоке, сообщение может не быть записано в случае сбоя приложения, что делает его бесполезным.

Причина в том, что вы всегда должны сбрасывать данные после каждого написания запись.

0
ответ дан 27 November 2019 в 20:45
поделиться

Здесь может помочь дополнительный уровень косвенного обращения.

Ваш первый вызов асинхронного метода может помещать сообщения в синхронизированную очередь и устанавливать событие, поэтому блокировки происходят в пуле потоков. , а не в ваших рабочих потоках - а затем еще один поток извлекает сообщения из очереди при возникновении события.

0
ответ дан 27 November 2019 в 20:45
поделиться

Я предлагаю начать с измерения фактическое влияние журналирования на производительность системы в целом (например, запуск профилировщика) и, возможно, переключение на что-то более быстрое, например log4net (я лично перешел на него с ведения журнала EntLib давным-давно).

Если это не работает, вы можете попробовать использовать этот простой метод из .NET Framework:

ThreadPool.QueueUserWorkItem

Ставит метод в очередь на выполнение. Метод выполняется, когда поток пула потоков становится доступным.

Подробности MSDN

Если и это не работает, вы можете прибегнуть к чему-то вроде предложения Джона Скита и фактически самостоятельно запрограммировать структуру асинхронного ведения журнала.

2
ответ дан 27 November 2019 в 20:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: