Запустите 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]
Я написал этот код некоторое время назад, не стесняйтесь использовать его.
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);
}
}
}
}
Некоторые преимущества:
Вот немного улучшенная версия, имейте в виду, что я провел очень небольшое тестирование, но она решает некоторые незначительные проблемы.
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();
}
}
Преимущества перед оригиналом:
Да, вам нужна очередь производителя / потребителя. У меня есть один пример этого в моем учебнике по потокам - если вы посмотрите мою страницу "deadlocks / monitor методов" , вы найдете код во второй половине.
Конечно, в сети есть и много других примеров - и .NET 4.0 будет поставляться с одним в фреймворке (скорее, более полнофункциональным, чем мой!). В .NET 4.0 вы, вероятно, включили бы ConcurrentQueue<T>
в BlockingCollection<T>
.
Версия на этой странице не является общей (она была написана давно назад), но вы, вероятно, захотите сделать ее общей - это будет тривиально.
Вы должны вызывать Produce
из каждого «нормального» потока и Consume
из одного потока, просто зацикливаясь и регистрируя все, что он потребляет. Вероятно, проще всего сделать потребительский поток фоновым потоком, поэтому вам не нужно беспокоиться об «остановке» очереди при выходе из приложения. Это означает, что есть удаленная возможность пропустить окончательную запись журнала (если она наполовину записывается при выходе из приложения) - или даже больше, если вы производите быстрее, чем оно может потреблять / log.
Я хотел сказать, что мой предыдущий пост был бесполезным. Вы можете просто установить 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);
}
}
Если вы имеете в виду очередь SHARED, то я думаю, что вам придется синхронизировать записи в нее, нажатия и всплывающие окна.
Но я все еще думаю, что стоит стремиться к дизайну общей очереди. По сравнению с IO ведения журнала и, вероятно, по сравнению с другой работой, выполняемой вашим приложением, краткая блокировка толчков и всплывающих окон, вероятно, не будет существенной.
Вот то, что я придумал ... также см. Ответ Сэма Шафрана. Этот ответ - вики сообщества, если есть какие-то проблемы, которые люди видят в коде и хотят обновить.
/// <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;
}
}
}
}
}
Если вы регистрируете что-то в отдельном потоке, сообщение может не быть записано в случае сбоя приложения, что делает его бесполезным.
Причина в том, что вы всегда должны сбрасывать данные после каждого написания запись.
Здесь может помочь дополнительный уровень косвенного обращения.
Ваш первый вызов асинхронного метода может помещать сообщения в синхронизированную очередь и устанавливать событие, поэтому блокировки происходят в пуле потоков. , а не в ваших рабочих потоках - а затем еще один поток извлекает сообщения из очереди при возникновении события.
Я предлагаю начать с измерения фактическое влияние журналирования на производительность системы в целом (например, запуск профилировщика) и, возможно, переключение на что-то более быстрое, например log4net (я лично перешел на него с ведения журнала EntLib давным-давно).
Если это не работает, вы можете попробовать использовать этот простой метод из .NET Framework:
ThreadPool.QueueUserWorkItem
Ставит метод в очередь на выполнение. Метод выполняется, когда поток пула потоков становится доступным.
Если и это не работает, вы можете прибегнуть к чему-то вроде предложения Джона Скита и фактически самостоятельно запрограммировать структуру асинхронного ведения журнала.