Наблюдение за наблюдением от другой потока [дублировать]C#

Место общения программистов C#
Ответить
Anonymous
 Наблюдение за наблюдением от другой потока [дублировать]

Сообщение Anonymous »

Примечание. Я публикую много кода для полностью воспроизводимого примера, но большая его часть здесь не актуальна, а просто для обеспечения необходимого контекста, большую часть кода можно пропустить.< /p>
Намерение: в моем приложении C#/WPF у меня есть Logger (реализованный как Singleton), который широко используется во всем приложении для регистрации различных поведение:

Код: Выделить всё

   public class Logger
{
public ReadOnlyCollection Log => _logRo;
private readonly ObservableCollection _log;
private readonly ReadOnlyObservableCollection _logRo;

private static Logger? _instance;
private Dictionary _logActors;

public static Logger GetInstance()
{
return _instance ??= new Logger();
}

public Logger()
{
_log = [];
_logRo = new(_log);

_logActors = new Dictionary
{
{ LogLevels.Trace,    [new ConsoleWriter(), new FileWriter()] },
{ LogLevels.Debug,    [new ConsoleWriter(), new FileWriter()] },
{ LogLevels.Info,     [new ConsoleWriter(), new FileWriter()] },
{ LogLevels.Warning,  [new ConsoleWriter(), new FileWriter()] },
{ LogLevels.Error,    [new ConsoleWriter(), new FileWriter()] },
{ LogLevels.Fatal,    [new ConsoleWriter(), new FileWriter()] },
};
}

public static void Add(LogLevels level, string message)
{
var instance = GetInstance();

LogEntry entry = new(level, message);
instance?._log.Add(entry);

List? actors = instance?._logActors[level];
if (actors == null) return;
foreach (var actor in actors)
{
actor.Perform(entry);
}
}

public void Clear()
{
_log.Clear();
}

private interface ILogActor
{
void Perform(LogEntry entry);
}

private class ConsoleWriter : ILogActor
{
public void Perform(LogEntry entry)
{
Console.WriteLine(entry.ToString());
}
}

private class FileWriter : ILogActor
{
public void Perform(LogEntry entry)
{
var folder = Utilities.FileOperations.GetAppPath();
const string name = "debug.log";
var path = folder + name;
Utilities.FileOperations.AppendToFile(path, entry.ToString() + Environment.NewLine);
}
}

public class LogEntry(LogLevels level, string message)
{
public DateTime Time { get; init; } = DateTime.Now;
public LogLevels Level { get; init; } = level;
public string Message { get; init; } = message;

public override string ToString()
{
List entryItems = [Time.ToString(CultureInfo.InvariantCulture), Utilities.EnumTools.GetEnumDescription(Level), Message];

return entryItems.Aggregate(string.Empty, (current, item) => current + (item + "\t"));
}
}
}

[Flags]
public enum LogLevels
{
[Description("TRACE")]
Trace = 1,
[Description("DEBUG")]
Debug = 2,
[Description("INFO")]
Info = 4,
[Description("WARNING")]
Warning = 8,
[Description("ERROR")]
Error = 16,
[Description("FATAL")]
Fatal = 32
}
У меня также есть страница WPF и окно, которое ее размещает, для отображения logger.log contents.
Страница:

Код: Выделить всё

public partial class LoggerPage : Page
{
LoggerPageViewModel vm;

public LoggerPage(AppSettingsData.GeneralSettings generalSettings)
{
InitializeComponent();
vm = new LoggerPageViewModel(generalSettings);
DataContext = vm;
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
var view = (CollectionView)CollectionViewSource.GetDefaultView(Log_ListView.ItemsSource);
view.Filter = UserFilter;
}

private bool UserFilter(object item)
{
var level = (item as Logger.LogEntry).Level;

if (vm.LogDisplayLevels.HasFlag(level))
{
return true;
}

return false;
}

private void CheckboxChanged(object sender, RoutedEventArgs e)
{
CollectionViewSource.GetDefaultView(Log_ListView.ItemsSource).Refresh();
}

public class LoggerPageViewModel
{
public ReadOnlyCollection Log { get; set; }
public LogLevels LogDisplayLevels { get; init; }

public LoggerPageViewModel(AppSettingsData.GeneralSettings generalSettings)
{
Log = Logger.GetInstance().Log;
LogDisplayLevels = generalSettings.DisplayLogLevels;
}
}
}

public class LogMaskValueConverter : IValueConverter
{
private LogLevels target;

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var mask = (LogLevels)parameter;
target = (LogLevels)value;
return (mask & target) != 0;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
target ^= (LogLevels)parameter;
return target;
}
}

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyApplication"
xmlns:gui="clr-namespace:MyApplication.GUI"
xmlns:loggingUtils="clr-namespace:LoggingUtils;assembly=LoggingUtils"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="350"
Title="LoggerPage" Loaded="Page_Loaded">




































































Окно:

Код: Выделить всё

public partial class LoggerWindow : Window
{
public LoggerWindow(AppSettingsData.GeneralSettings generalSettings)
{
InitializeComponent();
LogDisplay_Frame.Navigate(new LoggerPage(generalSettings));
}

private void ClearButton_Click(object sender, RoutedEventArgs e)
{
Logger.GetInstance().Clear();
}
}













< /code>
Теперь я хочу отобразить это окно, когда начинается приложение, и для того, чтобы обновить его содержимое в прямом эфире, когда приложение запускается без замораживания, обновление, как только новые записи добавляются в Logger .Log 
. Поэтому моя идея заключалась в том, чтобы запустить пользовательский и основной приложение в отдельных потоках. Насколько я понимаю, в приложениях WPF пользовательский интерфейс должен работать в основном потоке, поэтому вместо этого я запускаю все остальное в отдельном потоке: < /p>

Код: Выделить всё

public partial class App : System.Windows.Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);

var loggerWindow = new LoggerWindow(GeneralSettings);
loggerWindow.Show();
RunTestsAsync();
}

private async void RunTestsAsync()
{
await Task.Run(() =>
{
Tests.Run(); //Various calls that take ~1 minute to complete, with many calls to Logger.Add
});
}
}
Обратите внимание, что GeneralSettings на самом деле является более сложным детским классом AppSettings (не показан здесь), я пропустил его и упрощенные GeneralSettings здесь для воспроизводимости : < /p>

Код: Выделить всё

    public static class GeneralSettings
{
public LogLevels DisplayLogLevels { get; init; } = LogLevels.Info | LogLevels.Error | LogLevels.Fatal;
}
проблема : Когда я пытаюсь запустить это, я получаю ошибку на logger.add () , экземпляр строки? ._ addd ( Вход) :

Этот тип CollectionVie />
Как я понимаю, проблема заключается в том, что logger.log (asbustablecollection) обрабатывается двумя потоками - один, который его модифицирует, и один, который подписывается на Это события изменения контента, которые не допускаются. Это, однако, является моим фактическим намерением - я хочу, чтобы ядро ​​приложения продолжало запись в этот logger.log и для LoggerPage , чтобы немедленно заметить эти изменения и обновить пользовательский интерфейс. Я не знаю, как решить это. < /P>
Что я здесь делаю не так? Мой подход неверен? Если да, то как я должен справиться с этим? (Я попробовал это из фонового потока, в котором используется журнал, и он не сработал), поэтому я не уверен, можно ли это применить к моему коду без добавления инвазивных публичных вызовов в logger , чтобы вывести этот вызов в Потока пользовательского интерфейса. Я прошу повторно открыть этот вопрос, если я не ошибаюсь, и эта ситуация требует другого решения, но в случае, если я упускаю что-то очевидное, пожалуйста, прокомментируйте.

Подробнее здесь: https://stackoverflow.com/questions/793 ... her-thread
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»