您能否使对象的方法可供所有线程中的所有代码访问
本文关键字:线程 访问 代码 对象 方法 | 更新日期: 2023-09-27 17:55:17
感谢您对此问题的所有兴趣。 你们中的一些人要求更清楚地了解所涉及的代码,所以为了提供更多信息,我将对其进行编辑以提供更多细节。
关于我之前的问题,我正在尝试在 WPF 窗口中模拟基本控制台(仅限文本输出)。 它旨在与在后台运行大量代码的程序一起使用,这些代码在单独的线程上运行。 此代码也严重依赖于 while 循环,因此我的计划是将 WPF 控制台窗口保留在主线程上(以及可能需要的任何其他 GUI 窗口),并在单独的线程上执行所有代码。
该窗口有一个 WriteLine 方法,如下所示:
mainConsole.WriteLine("This is a message for the user.", SomeSender);
代码的其余部分将需要定期调用此方法。
附加信息:
窗口本身由一个包裹在滚动器中的文本块组成。 窗口的 WriteLine 方法将消息和格式(字体、字体大小和颜色 - 取决于消息的发件人是谁)添加到包含此信息的对象列表中,然后显示这些消息的列表,包括其格式)作为文本块的内容。 该方法完全按预期工作,因此不需要重写,只需要可访问即可。
我试图使这个描述尽可能简洁。 有关更多信息,请参阅我之前的问题。
所以我现在的问题是:有没有一种有效的方法可以使窗口的 WriteLine 方法可用于任何类的所有线程,从而使我能够像 Console.WriteLine() 一样使用它?
虽然您有多种选择,但听起来,在您的情况下,对于任何地方的任何人都可以写入您的控制台确实有意义。 鉴于此,我将创建如下内容:
public class MyConsole
{
public static event Action<string> TextWritten;
public static void Write(object obj)
{
string text = (obj ?? "").ToString();
if (TextWritten != null)
TextWritten(text);
}
public static void WriteLine(object obj)
{
Write(obj + "'n");
}
}
然后,让您的控制台窗体订阅 TextWritten
事件,并在写入文本时将该文本写入控制台。 (请确保先封送到 UI 线程。
与让此类直接处理表单相比,在这里使用 and event 的主要优点是,您可以轻松添加其他事件处理程序,允许您与标准输入/输出交互、向文件添加其他日志记录、一次打开多个控制台表单等。 这种灵活性对于调试(即对平面文件的额外写出)和生产(允许通过标准输入/输出进行更轻松的重定向)都很有用。
您似乎正在尝试编写一个日志记录服务,该服务允许您从代码中的任何位置访问日志。您提到了线程,因此您必须注意并相应地处理该同步。
我将首先创建一个ILogger
接口,如下所示:
public interface ILogger
{
void Log(string line);
void Log(string format, params object[] args);
}
然后是一个适当的Logger
基类:
public abstract class Logger : ILogger
{
public abstract void Log(string line);
public virtual void Log(string format, params object[] args)
{
Log(string.Format(format, args));
}
}
当然,您需要一个实际的实现:
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class ConcurrentLogger : Logger, ILogger, IDisposable
{
bool isDisposed;
BlockingCollection<string> loggedLines;
Action<string> callback;
public ConcurrentLogger(Action<string> callback)
{
if (callback == null)
throw new ArgumentNullException("callback");
var queue = new ConcurrentQueue<string>();
this.loggedLines = new BlockingCollection<string>(queue);
this.callback = callback;
StartMonitoring();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool isDisposing)
{
if (isDisposed) return;
if (isDisposing)
{
if (loggedLines != null)
loggedLines.CompleteAdding();
}
isDisposed = true;
}
public override void Log(string line)
{
if (!loggedLines.IsAddingCompleted)
loggedLines.Add(line);
}
protected virtual void StartMonitoring()
{
Task.Factory.StartNew(() =>
{
foreach (var line in loggedLines.GetConsumingEnumerable())
{
if (callback != null)
callback(line);
}
loggedLines.Dispose();
}, TaskCreationOptions.LongRunning);
}
}
对于全局访问,您将需要一个Singleton
类,因此我将LogManager
类设置为:
public sealed class LogManager : ILogger
{
#region Singleton
static readonly LogManager instance = new LogManager();
public static LogManager Current { get { return instance; } }
private LogManager() { } // Disallow creating instances.
#endregion
ILogger logger;
public ILogger Logger { get { return logger; } }
public void StartLogging(ILogger logger)
{
if (logger == null)
throw new ArgumentNullException("logger");
this.logger = logger;
}
public void StopLogging(bool dispose = true)
{
var previousLogger = this.logger as IDisposable;
this.logger =null;
if (previousLogger != null && dispose)
previousLogger.Dispose();
}
public void Log(string line)
{
if (logger != null) logger.Log(line);
}
public void Log(string format, params object[] args)
{
if (logger != null) logger.Log(format, args);
}
}
通过一些快速初始化:
void InitializeLog()
{
var log = new ConcurrentLogger(LogToTextBox);
LogManager.Current.StartLogging(log);
}
void LogToTextBox(string line)
{
if (!CheckAccess())
{
this.Dispatcher.BeginInvoke((Action<string>)LogToTextBox,
DispatcherPriority.Background,
line);
return;
}
logTextBox.AppendText(line + Environment.NewLine);
}
然后,在代码中的任何位置都可以调用:LogManager.Current.Log(...);
创建一个静态类,该类保存 WriteLine 方法和一个引用窗口、控件或 writeline 方法中所需的任何内容的属性。然后将一些代码添加到 MainWindow 构造函数或加载的事件中,以将 Reference-Property 设置为所需的项。之后,您可以从任何地方使用Writeline。
顺便说一句:使用带有实例getter的静态MainViewModel可能会更干净,将MainWindow的DataContext绑定到此ViewModel并使用MVVM模式。然后,您只需设置一些 ConsoleOutput 属性或从您想要的任何位置调用 AddLine 方法甚至命令,而不必知道视图如何显示它。您可以使用单元测试测试您的应用程序,您可以更改视觉表示形式,...所有这些都不会触及应用程序的逻辑。
namespace {yourrootnamespace}
{
namespace GlobalMethods
{
static class ConsoleMethods
{
mainConsole mc;
public static WriteLine(string msg, object sender)
{
lock (this)
{
mc.WriteLine(msg, sender)
}
}
static ConsoleMethods()
{
mc = new mainConsole();
}
//more methods
}
}
然后:using {yourrootnamespace}.GlobalMethods;
或者让这些方法接受一个 mainConsole 参数,然后使用它来调用。