1

我在使用插件架构的 c# 2.0 中有一个完善的控制台应用程序。截至目前,该程序使用可以运行多个实例的基本多线程。线程被创建并继续运行,直到应用程序停止。

每个实例都可以加载自己的各种插件并单独配置。插件继承自基本插件。这个系统多年来一直像魅力一样工作。插件是事件驱动的,它们都会读取各种事件以查看它们是否被调用,如果没有,它们会返回并让下一个插件读取事件以查看它们是否被调用触发。

这个系统已经工作了很多年。但是,我想进一步扩大多线程的范围,以允许插件以异步方式而不是同步方式监听事件。这种设置的缺点之一是,一旦插件触发并完成工作,它就会锁定实例。当下一个事件被触发时,它必须等待前一个工作完成。然后它将允许进行下一个过程。

我想做的是执行插件,而不必等待进程结束,然后再继续下一个进程以由事件开始。

我暂时坚持使用.Net 2.0,并且必须在该框架中找到解决方案。我查看了许多示例,但找不到符合标准的示例。问题之一是每个插件都有自己的处理时间,并且无法计算来跟踪插件完成的百分比。插件将在完成后开始和结束其进程。根据事件的参数和插件,它可能需要任何时间范围才能完成。

我的问题是在插件由事件执行的情况下处理多线程的最佳方法是什么。我查看了诸如http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.80).aspx之类的页面,我可以弄清楚在哪里可以有一个入口点事件驱动的插件架构。

如果有人有任何线索,我将不胜感激。多年来,缺乏这种方式的多线程一直是该应用程序的致命弱点。

插件库:这些包含一些由事件触发的功能:

using System;
using VhaBot.Communication;

namespace VhaBot
{

/// <summary>
///     Plugin BaseClass, must be inherited by all plugins
/// </summary>
public abstract class PluginBase : MarshalByRefObject
{
    private bool _locked;
    private string _name;
    private string _internalName;
    private int _version;
    private string _author;
    private string[] _contributors;
    private string _description;
    private PluginState _defaultState;
    private string[] _dependencies;
    private Command[] _commands;

    /// <summary>
    ///     Friendly display name of plugin
    /// </summary>
    /// <example>
    ///     <code>
    ///  this.Name = "Message of the Day";
    ///   </code>
    /// </example>
    public string Name
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _name = value;
        }
        get { return _name; }
    }

    /// <summary>
    ///     Internal name of the plugin
    /// </summary>
    /// <example>
    ///     <code>
    ///  this.InternalName = "VhMotd";
    ///   </code>
    /// </example>
    public string InternalName
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _internalName = value.ToLower();
        }
        get { return _internalName; }
    }

    /// <summary>
    ///     Pluigin Version
    /// </summary>
    /// <remarks>
    ///     Versions are stored as integers only. Version 1.0.0 would have a value of 100
    /// </remarks>
    /// <example>
    ///     <code>
    /// this.Version = 100;
    ///  </code>
    /// </example>
    public int Version
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _version = value;
        }
        get { return _version; }
    }

    /// <summary>
    ///     Author of the plugin
    /// </summary>
    /// <example>
    ///     <code>
    /// this.Author = "Vhab";
    ///  </code>
    /// </example>
    public string Author
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _author = value;
        }
        get { return _author; }
    }

    /// <summary>
    ///     List of contributors to the development of the plugin.
    /// </summary>
    /// <example>
    ///     <code>
    ///  this.Contributors = new string[] { "Iriche", "Kilmanagh" };
    ///   </code>
    /// </example>
    public string[] Contributors
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _contributors = value;
        }
        get
        {
            if (_contributors != null)
            {
                return _contributors;
            }
            return new string[0];
        }
    }

    /// <summary>
    ///     Description of the plugin
    /// </summary>
    /// <example>
    ///     <code>
    ///  this.Description = "Provides an interface to the user to view who is online and/or on the private channel.";
    ///   </code>
    /// </example>
    public string Description
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _description = value;
        }
        get { return _description; }
    }

    /// <summary>
    ///     The default <see cref="VhaBot.PluginState" /> of the plugin
    /// </summary>
    /// <example>
    ///     <code>
    /// this.DefaultState = PluginState.Installed;
    /// </code>
    /// </example>
    /// <seealso cref="VhaBot.PluginState" />
    public PluginState DefaultState
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _defaultState = value;
        }
        get { return _defaultState; }
    }

    /// <summary>
    ///     List of other plugins that a plugin is dependent on to function
    /// </summary>
    /// <remarks>
    ///     Plugins are referred to using their internal names. See <see cref="VhaBot.PluginBase.InternalName" />
    /// </remarks>
    /// <example>
    ///     <code>
    /// this.Dependencies = new string[] { "vhItems" };
    /// </code>
    /// </example>
    public string[] Dependencies
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _dependencies = value;
        }
        get
        {
            if (_dependencies != null)
            {
                return _dependencies;
            }
            return new string[0];
        }
    }

      public Command[] Commands
    {
        set
        {
            if (_locked)
            {
                throw new Exception();
            }
            _commands = value;
        }
        get
        {
            if (_commands != null)
            {
                return _commands;
            }
            return new Command[0];
        }
    }

    internal void Init()
    {
        _locked = true;
    }

    /// <summary>
    ///     A plugin has loaded in response to <see cref="VhaBot.ShellModules.Plugins.Load" />
    /// </summary>
    /// <param name="bot"></param>
    /// ///
    /// <remarks>Code inside this method will be executed when a plugin is loading</remarks>
    public virtual void OnLoad(BotShell bot)
    {
    }

    /// <summary>
    ///     A plugin has unloaded in response to <see cref="VhaBot.ShellModules.Plugins.Unload" />
    /// </summary>
    /// <param name="bot"></param>
    /// <remarks>Code inside this method will be executed when a plugin is unloading</remarks>
    public virtual void OnUnload(BotShell bot)
    {
    }

    /// <summary>
    ///     A plugin has installed in response to <see cref="VhaBot.ShellModules.Plugins.Install" />
    /// </summary>
    /// <param name="bot"></param>
    public virtual void OnInstall(BotShell bot)
    {
    }

    /// <summary>
    ///     A plugin as been uninstalled in response to <see cref="VhaBot.ShellModules.Plugins.Uninstall" />
    /// </summary>
    /// <param name="bot"></param>
    public virtual void OnUninstall(BotShell bot)
    {
    }

    /// <summary>
    ///     A plugin has been upgraded (Unused)
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="version"></param>
    /// <remarks>This function is not active</remarks>
    public virtual void OnUpgrade(BotShell bot, Int32 version)
    {
    }

    /// <summary>
    ///     Response to a command
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="e"></param>
    public virtual void OnCommand(BotShell bot, CommandArgs e)
    {
    }

    /// <summary>
    ///     Response to an unauthorized command
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="e"></param>
    public virtual void OnUnauthorizedCommand(BotShell bot, CommandArgs e)
    {
    }

    /// <summary>
    ///     Response to a command help query <see cref="VhaBot.ShellModules.Commands.GetHelp." />
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="command"></param>
    /// <returns></returns>
    /// <remarks>Code inside this method will be executed when help is requested</remarks>
    public virtual string OnHelp(BotShell bot, string command)
    {
        return null;
    }

    /// <summary>
    ///     Response to a custom configuration
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual string OnCustomConfiguration(BotShell bot, string key)
    {
        return null;
    }

    /// <summary>
    ///     Response to a plugin message
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="message"></param>
    public virtual void OnPluginMessage(BotShell bot, PluginMessage message)
    {
    }

    /// <summary>
    ///     Response to a bot message
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="message"></param>
    public virtual void OnBotMessage(BotShell bot, BotMessage message)
    {
    }

    /// <summary>
    ///     Returns display name of bot and current version
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return Name + " v" + Version;
    }

    /// <summary>
    ///     There is no information to document this command
    /// </summary>
    /// <param name="bot"></param>
    /// <param name="args"></param>
    public void FireOnCommand(BotShell bot, CommandArgs args)
    {
        try
        {
            if (args.Authorized)
                OnCommand(bot, args);
            else
                OnUnauthorizedCommand(bot, args);
        }
        catch (Exception ex)
        {
            CommandArgs e = args;
            var window = new RichTextWindow(bot);
            window.AppendTitle("Error Report");

            window.AppendHighlight("Error: ");
            window.AppendNormal(ex.Message);
            window.AppendLinkEnd();
            window.AppendLineBreak();

            window.AppendHighlight("Source: ");
            window.AppendNormal(ex.Source);
            window.AppendLinkEnd();
            window.AppendLineBreak();

            window.AppendHighlight("Target Site: ");
            window.AppendNormal(ex.TargetSite.ToString());
            window.AppendLinkEnd();
            window.AppendLineBreak();

            window.AppendHighlight("Stack Trace:");
            window.AppendLineBreak();
            window.AppendNormal(ex.StackTrace);
            window.AppendLinkEnd();
            window.AppendLineBreak();

            bot.SendReply(e,
                          "There has been an error while executing this command »» " +
                          window.ToString("More Information"));
            BotShell.Output("[Plugin Execution Error] " + ex);
        }
    }
}
}

事件类:

 namespace VhaBot.ShellModules
{
/// <summary>
///     VhaBot Events
/// </summary>
public class Events
{
    public event BotStateChangedHandler BotStateChangedEvent;
    public event ChannelJoinEventHandler ChannelJoinEvent;

    public event UserJoinChannelHandler UserJoinChannelEvent;
    public event UserLeaveChannelHandler UserLeaveChannelEvent;

    public event UserLogonHandler UserLogonEvent;
    public event UserLogoffHandler UserLogoffEvent;

    public event PrivateMessageHandler PrivateMessageEvent;
    public event PrivateChannelMessageHandler PrivateChannelMessageEvent;
    public event ChannelMessageHandler ChannelMessageEvent;

    public event MemberAddedHandler MemberAddedEvent;
    public event MemberRemovedHandler MemberRemovedEvent;
    public event MemberUpdatedHandler MemberUpdatedEvent;

    public event AltAddedHandler AltAddedEvent;
    public event AltRemovedHandler AltRemovedEvent;

    /// <summary>
    ///     A message was sent to the IRC channel in response to a <see      cref="VhaBot.BotShell.SendIrcMessage" /> request
    /// </summary>
    public event IrcMessageHandler IrcMessageEvent;

    public event ConfigurationChangedHandler ConfigurationChangedEvent;


    internal void OnBotStateChanged(BotShell bot, BotStateChangedArgs e)
    {
        if (BotStateChangedEvent != null)
            try
            {
                BotStateChangedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnChannelJoin(BotShell bot, ChannelJoinEventArgs e)
    {
        if (ChannelJoinEvent != null)
            try
            {
                ChannelJoinEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnUserJoinChannel(BotShell bot, UserJoinChannelArgs e)
    {
        if (UserJoinChannelEvent != null)
            try
            {
                UserJoinChannelEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnUserLeaveChannel(BotShell bot, UserLeaveChannelArgs e)
    {
        if (UserLeaveChannelEvent != null)
            try
            {
                UserLeaveChannelEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnUserLogon(BotShell bot, UserLogonArgs e)
    {
        if (UserLogonEvent != null)
            try
            {
                UserLogonEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnUserLogoff(BotShell bot, UserLogoffArgs e)
    {
        if (UserLogoffEvent != null)
            try
            {
                UserLogoffEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnPrivateMessage(BotShell bot, PrivateMessageArgs e)
    {
        if (PrivateMessageEvent != null)
            try
            {
                PrivateMessageEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnPrivateChannelMessage(BotShell bot, PrivateChannelMessageArgs e)
    {
        if (PrivateChannelMessageEvent != null)
            try
            {
                PrivateChannelMessageEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnChannelMessage(BotShell bot, ChannelMessageArgs e)
    {
        if (ChannelMessageEvent != null)
            try
            {
                ChannelMessageEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnMemberAdded(BotShell bot, MemberAddedArgs e)
    {
        if (MemberAddedEvent != null)
            try
            {
                MemberAddedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnMemberRemoved(BotShell bot, MemberRemovedArgs e)
    {
        if (MemberRemovedEvent != null)
            try
            {
                MemberRemovedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnMemberUpdated(BotShell bot, MemberUpdatedArgs e)
    {
        if (MemberUpdatedEvent != null)
            try
            {
                MemberUpdatedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnAltAdded(BotShell bot, AltAddedArgs e)
    {
        if (AltAddedEvent != null)
            try
            {
                AltAddedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnAltRemoved(BotShell bot, AltRemovedArgs e)
    {
        if (AltRemovedEvent != null)
            try
            {
                AltRemovedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnConfigurationChanged(BotShell bot, ConfigurationChangedArgs e)
    {
        if (ConfigurationChangedEvent != null)
            try
            {
                ConfigurationChangedEvent(bot, e);
            }
            catch
            {
            }
    }

    internal void OnIrcMessage(BotShell bot, IrcMessageArgs e)
    {
        if (IrcMessageEvent != null)
        {
            IrcMessageEvent(bot, e);
        }
        try
        {
        }
        catch
        {
        }
        }
    }
}
4

1 回答 1

1

由于您对系统的描述有点模糊,因此我没有什么可继续的,但我会试一试。

从你的描述看来你有一些插件,说

interface IPlugin {
   PluginResult ReadAndExecuteEvents(Events e);

   // Added asynchronous methods.
   IAsyncResult BeginReadAndExecuteEvents(Events e, AsyncCallback cb, Object state);
   PluginResult EndReadAndExecuteEvents(IAsyncResult result);
}

class PluginResult 
{
    public Boolean Stop;
    // etc.
}

您似乎也没有使用 .NET事件,而是使用某种Event类/枚举。

您的旧代码似乎是这样的:

   foreach (var eventList in ReadEvents())
           foreach (var plugin in pluginList)
              if (plugin.ReadAndExecuteEvents(eventList).Stop)
                 break;

您可以使此异步执行以下操作:

 foreach (var eventList in ReadEvents())
     {
        // It seems this is what you want, only one event processed at a time by an "instance"? So block here until unlocked.
        LockProcess();

        var pluginIndex = 0;
        AsyncCallback handleResult = null;
        handleResult = delegate(IAsyncResult result)
        {
           if (pluginList[pluginIndex].EndReadAndExecuteEvents(result).Stop)
              goto STOP;

           pluginIndex += 1;

           if (pluginIndex == pluginList.Count)
              goto STOP;

           Events e = (Events)result.AsyncState;

           pluginList[pluginIndex].BeginReadAndExecuteEvents(e, handleResult, e);
           return;

        STOP:
           UnlockProcess();
        };

        pluginList[0].BeginReadAndExecuteEvents(eventList, handleResult, eventList);
     }

因此,在 .NET 2 样式中,您可以添加一些 BeginXxx 方法并在其 AsyncCallback 中执行您的操作。当然,由实际的插件来完成它的多线程/异步性,比如它是否通过使用BeginWritetoFileStream等来写入文件。

我在这里很方便地忽略了异常处理。

因此,为了让您的整个应用程序使用这种异步性,您可以将此代码放入一个BeginRunEvents方法中,例如,遵循相同的“APM”模式。然后,您可以根据需要将其安排到线程池。

如果这根本不是您要查找的内容,请提供更多代码示例/信息。

于 2013-05-19T10:17:39.173 回答