【问题标题】:how do i let this run without affecting others?我如何让它运行而不影响其他人?
【发布时间】:2018-10-10 14:28:31
【问题描述】:

所以,我正在制作一个不和谐机器人,它能够在一定时间后改变角色的颜色。它运行正常,但是,在运行此任务时,机器人无法注册其他命令或运行任何其他工作。 (运行停止命令时它不会停止,因为它首先无法进入那里)。

我希望这里有人可以帮助解决这个问题。

下面是部分代码:

[Group("rainbow")]
public class Rainbow : ModuleBase<SocketCommandContext>
{
    public Boolean enabled;

    [Command, RequireUserPermission(GuildPermission.ManageRoles)]
    public async Task rainbowAsync(SocketRole role, int milliseconds)
    {
        if (milliseconds >= 500) {
                 enabled = true;       
            while (enabled)
            {
                Random rnd = new Random();
                double id = rnd.NextDouble() * 7;

                if (id < 1)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.Red);
                }
                else if (id < 2)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.Orange);
                }
                else if (id < 3)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.LightOrange);
                }
                else if (id < 4)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.Green);
                }
                else if (id < 5)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.Blue);
                }
                else if (id < 6)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.DarkBlue);
                }
                else if (id < 7)
                {
                    await role.ModifyAsync(a => a.Color = Discord.Color.Purple);
                }

                await Task.Delay(milliseconds);
            }
        }
        else { await ReplyAsync("Time value must be higher than 500ms"); }

    }

    [Command("stop"), RequireUserPermission(GuildPermission.ManageRoles)]
    public async Task StopAsync()
    {
        enabled = false;
        await ReplyAsync("Rainbow effect stopped");
    }
}

【问题讨论】:

  • 您确实需要提供minimal reproducible example。看看How to Ask
  • “运行停止命令时它不会停止,因为它一开始就无法进入”我们需要知道发生了什么阻止它到达那里.. . . 像,代码看起来像什么,无法到达 StopAsync()。
  • 其余代码应该没问题,因为其他具有类似结构的命令运行良好
  • 它不能做任何其他事情,因为你的任务卡在一个无限的while循环中。任务需要完成或停止才能处理另一个任务。我认为您的 StopAsync() 已排队,但因此无法执行。您可能可以通过使用检查“停止”是否在消息上下文中并在主类中将启用的布尔值设置为 false 的方法来解决此问题。

标签: c# discord.net


【解决方案1】:

正如亚历克斯在评论中提到的,

您的命令进入无限循环。

if (milliseconds >= 500) {
    enabled = true;       
    while (enabled)
    {
        //Code
        await Task.Delay(milliseconds);
    }
}

这会将您的命令置于无限循环中,从而阻止其他命令触发。这会阻止其他命令在 Async 环境中触发的原因是因为 Async 仍然只是一个单线程,并且 Discord.net 调用您的命令的默认方式是 await Command();

当等待异步调用时,它会阻塞调用,直到任务完成。由于无限循环,您的任务永远不会完成。

有几种不同的方法来处理这个问题,但我建议处理它的方法是安排重新着色。研究如何安排任务在每 x 时间范围内发生。

举个例子

public class TimerTask
{
    private readonly Timer _taskTimer;

    public TimerTask(Action action, int interval = 10000)
    {
        _taskTimer = new Timer { AutoReset = true, Interval = interval };
        _taskTimer.Elapsed += (_, __) => action();
    }
    public void Start() { _taskTimer.Start(); }

    public void Stop() { _taskTimer.Stop(); }
}

【讨论】:

  • 仅仅因为异步代码不需要多个线程来运行并不意味着异步代码会在持续时间内阻塞启动操作的代码。事实上,如果确实如此,那么根据定义,它将是一个同步方法。异步方法的定义是它立即返回给调用者,而不是等待实际操作完成。
  • 除非他的命令的默认 RunMode 设置为 RunMode.Async 然后命令本身被称为等待。等待调用具有无限循环的方法确实会阻塞。将默认运行模式设置为 RunMode.Async 可能会导致更大的问题,例如重入问题和竞争条件。我建议并且确实建议只编写一个定时调度程序来处理随时间发生的火灾事件,而不是增加其他问题的可能性,只是为了在命令中运行无限循环。我的回答有点错误。我会稍微改变一下。
  • 同样,await 不会阻塞当前线程。它是异步的。它没有阻塞任何线程,它只是安排一些工作在以后运行没有线程在等待它
  • 它正在阻塞当前上下文。自己尝试一下...启动一个异步主程序(您可以使用 C# 7.2 或更高版本)并使用无限循环调用等待的任务。过去的任何代码都不会触发。他的代码也发生了同样的事情。该命令被调用等待并阻塞上下文。不会触发其他命令(因为它正在等待上下文)。
  • 当然,该方法的调用者完全有可能在异步操作完成后安排工作发生,并且您可能不会等待它在异步操作期间推迟该工作,但是这不是阻塞线程.
猜你喜欢
  • 2012-11-07
  • 2021-09-07
  • 2015-06-26
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 2021-11-18
相关资源
最近更新 更多