C#WPF程序按钮单击运行任务,直到另一个按钮单击停止或直到取消令牌有效

C#WPF程序按钮单击运行任务,直到另一个按钮单击停止或直到取消令牌有效,c#,wpf,multithreading,C#,Wpf,Multithreading,我正在创建一个WPF应用程序,其中我希望有一个全局bool,我假设,在第一次单击按钮时,我会将此bool设置为true,并希望它运行一个任务(连续调用API方法),直到我再次单击按钮,它停止它。最好的方法是什么 private bool running = false; private async void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e) { if (!running)

我正在创建一个WPF应用程序,其中我希望有一个全局bool,我假设,在第一次单击按钮时,我会将此bool设置为true,并希望它运行一个任务(连续调用API方法),直到我再次单击按钮,它停止它。最好的方法是什么

    private bool running = false;

    private async void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
    {
        if (!running)
        {
            running = true;
        }
        else
            running = false;

        if (running)
        {
            RunningNrunnin(running);
            //tradeClient.GetTradeHistory();
        }
    }

    public void RunningNrunnin(bool running)
    {
        if (running)
        {
            Task task = new Task(() =>
            {
                while (running)
                {
                    GetTradeHistory();
                    Thread.Sleep(2000);
                }
            });
            task.Start();
        }
    }
添加到下面

我想反复调用一个方法,直到用户在后台的线程上创建一个取消请求。我现在有它,所以我可以调用一个操作(计数器)并每秒更新GUI,但当我尝试使用方法调用执行同样的操作时,它只执行一次

// Here is the method I want to call continously until canceled
private async void HistoryTest()
        {
            cancellationToken = new CancellationTokenSource();

            task = Task.Factory.StartNew(async () =>
            {
                while (true)
                {
                    cancellationToken.Token.ThrowIfCancellationRequested();

                    await Client2.GetHistory();
                    await Task.Delay(2000);
                }
            }, cancellationToken.Token);

        }
public async Task GetHistory()
        {
            try
            {
                var response = await Client.Service.GetDataAsync
                    (
                        ProductType.BtcUsd,
                        5,
                        1
                    );
            }
            catch(Exception)
            {
                throw;
            }
        }

我制作了一个小的控制台测试应用程序来测试这一点,因此我必须更改方法签名(静态),并且不能在控制台上使用
按钮单击
。我通过在编程的“按钮点击”之间设置睡眠来模拟按钮点击

这可能会让你开始

    private static bool isRunning = false;
    private static int clickCounter = 0;
    private static int iterationsCounter = 0;

    static void Main(string[] args)
    {
        Console.WriteLine(“Start”);
        for(int i = 0; i < 7; i++)
        {
            BtnTrade1_Buy_Click();
            System.Threading.Thread.Sleep(1000);
        }
        Console.WriteLine(“END”);
    }


    private static async Task BtnTrade1_Buy_Click()
    {
        iterationsCounter = 0;
        isRunning = !isRunning;
        Console.WriteLine($"Ha: {isRunning} {clickCounter++}");
        await RunningNrunnin();
    }



    private static async Task RunningNrunnin()
    {
        await Task.Run(() => Runit());
    }


    private static void Runit()
    {
        while (isRunning)
        {
            GetTradeHistory();
            System.Threading.Thread.Sleep(100);
        }
    }


    private static void GetTradeHistory()
    {
        Console.WriteLine($"Hello Test {iterationsCounter++}");
    }
private static bool isRunning=false;
私有静态int clickCounter=0;
私有静态int迭代计数器=0;
静态void Main(字符串[]参数)
{
控制台写入线(“开始”);
对于(int i=0;i<7;i++)
{
BtnTrade1_购买_点击();
系统线程线程睡眠(1000);
}
控制台。写入线(“结束”);
}
私有静态异步任务BtnTrade1\u购买\u单击()
{
迭代计数器=0;
isRunning=!isRunning;
WriteLine($“Ha:{isRunning}{clickCounter++}”);
等待运行n运行();
}
私有静态异步任务RunningNrunnin()
{
等待任务。运行(()=>Runit());
}
私有静态void Runit()
{
同时(正在运行)
{
GetTradeHistory();
系统线程线程睡眠(100);
}
}
私有静态无效GetTradeHistory()
{
WriteLine($“Hello Test{IterationCounter++}”);
}
当然,您不需要所有的计数器和
Console.WriteLine()
之类的东西。他们在那里让你想象正在发生的事情


如果您需要更多信息,请告诉我。

除了切换
isRunning
字段外,您不需要在
BtnTrade1\u Buy\u单击事件处理程序中执行任何其他操作:

private bool _isRunning;

private void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
    _isRunning = !_isRunning;
}
在循环中获取交易历史的
任务
,只需启动一次。您可以在
窗口\u Loaded
事件中启动它。将
任务
存储在私有字段中是一个好主意,以防您决定在某个时候等待它,但如果您正在处理任务内部的异常,则不必这样做

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    _ = StartTradeHistoryLoopAsync(); // Fire and forget
}

private async Task StartTradeHistoryLoopAsync()
{
    while (true)
    {
        var delayTask = Task.Delay(2000);
        if (_isRunning)
        {
            try
            {
                await Task.Run(() => GetTradeHistory()); // Run in the ThreadPool
                //GetTradeHistory(); // Alternative: Run in the UI thread
            }
            catch (Exception ex)
            {
                // Handle the exception
            }
        }
        await delayTask;
    }
}
当窗口关闭时,不要忘记停止任务

private void Window_Closed(object sender, EventArgs e)
{
    _isRunning = false;
}
这将停止对
GetTradeHistory()
的调用,但不会停止循环。您可能需要再添加一个private
bool
字段来控制循环本身:

while (_alive) // Instead of while (true)

当我调试我的程序时,它是否像你的代码一样执行了一次,并使GUI返回,它不会继续执行…@Bigbear你是否尝试过在没有附加调试程序的情况下运行程序?使用Ctrl+F5。那么一个简单的问题,您会推荐每1-2秒进行一次这样的方法调用,还是推荐WebSocket进行连续数据调用?(交易订单),只要我使用Task.Run(),它是否在后台运行?所以我可以继续使用主GUI进行其他调用?@Bigbear我还没有使用WebSockets,所以我无法给出明智的建议。我想WebSocket更轻,但它可能更复杂。无论如何,不要引用我的话。:-)