Фоновый рабочий и таймер в приложении на панели задач C# - PullRequest
1 голос
/ 06 мая 2020

Это невероятно простое приложение на панели задач - с использованием ApplicationContext и нескольких руководств, которые я нашел в Интернете.

Цель приложения - запросить небольшой REST API и показать сообщение box пользователю по заданному результату. Мне нужно, чтобы запрос API в фоновом режиме l oop выполнялся каждые 10 секунд или что-то подобное. Это отчет о данных, которые я сделал доступными через другую службу.

Я прочитал кое-что, и мне кажется, что BackgroundWorker и Таймер - подходящий вариант, но я не знаю, где теперь go. Как именно этого добиться? Сначала я попытался добавить while(true) l oop к TaskTrayApplicationContext, но он просто создал бесконечное l oop, в результате чего вы не могли больше ничего делать с приложением.

namespace TaskTrayApplication
{
public class TaskTrayApplicationContext : ApplicationContext
{
    NotifyIcon notifyIcon = new NotifyIcon();
    Configuration configWindow = new Configuration();

    public TaskTrayApplicationContext()
    {
        MenuItem configMenuItem = new MenuItem("Configuration", new EventHandler(ShowConfig));
        MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));

       notifyIcon.Icon = TaskTrayApplication.Properties.Resources.AppIcon;
        notifyIcon.DoubleClick += new EventHandler(ShowMessage);
        notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { configMenuItem, exitMenuItem });
        notifyIcon.Visible = true;
    }


    void ShowMessage(object sender, EventArgs e)
    {
        // Only show the message if the settings say we can.
        if (TaskTrayApplication.Properties.Settings.Default.ShowMessage)
            MessageBox.Show("This is the Serenity TaskTray Agent.");
    }

    void ShowConfig(object sender, EventArgs e)
    {
        // If we are already showing the window meerly focus it.
        if (configWindow.Visible)
            configWindow.Focus();
        else
            configWindow.ShowDialog();
    }

    void Exit(object sender, EventArgs e)
    {
        // We must manually tidy up and remove the icon before we exit.
        // Otherwise it will be left behind until the user mouses over.
        notifyIcon.Visible = false;

        Application.Exit();
    }
}

}

И Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace TaskTrayApplication
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            // Instead of running a form, we run an ApplicationContext.
            Application.Run(new TaskTrayApplicationContext());
        }
    }
}

1 Ответ

2 голосов
/ 06 мая 2020

Многопоточность сложна, параллелизм затруднен. Фоновый рабочий и System.Timers - это конструкции, которые выполняются в собственном потоке.

winforms не допускает взаимодействия между потоками, которым принадлежит элемент управления (читай: который создал элемент управления), и потоками, которые этого не делают. Это отдельная тема, я не буду сейчас вдаваться в подробности - есть хороший материал, чтобы прочитать, почему это и как go об этом: https://visualstudiomagazine.com/articles/2010/11/18/multithreading-in-winforms.aspx

Есть инструменты для help, один из них - диспетчертаймер:

https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netcore-3.1

Это специальный таймер, который вместо своего собственного потока планирует задачи в основном потоке. Основной поток в приложении winforms обрабатывает отрисовку элементов управления, показывая различные windows и c. например, этому 'принадлежат' все элементы управления.

Пример можно увидеть на msdn, я использовал его здесь, чтобы показать вам, что вы можете сделать:

public class TaskTrayApplicationContext : ApplicationContext
{
    ...


    DispatcherTimer dispatcherTimer;

    public TaskTrayApplicationContext()
    {
        ...

        dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
        dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
        dispatcherTimer.Interval = new TimeSpan(0,0,1);
        dispatcherTimer.Start();
    }


    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        // Fetch your data via a rest api
        var myData = MyDataFunction();

        // check and show dialog if the data is not okay
        if(myData.Result.Value = 'NOT_OKAY!')
           ShowMessage(this, myData.Result); // or something.
    }

    ...


Теперь, поскольку здесь не используется второй поток, это означает, что основной поток пользовательского интерфейса может быть заблокирован от рисования windows, реагируя на ввод пользователя et c. потому что он занят работой в функции timer_tick. Это может произойти, например, если ваш вызов для отдыха занимает много времени.

Это приведет к зависанию вашего приложения и его зависанию. Это может быть проблемой, но, скорее всего, не будет, так что давайте сожжем этот мост, когда доберемся до него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...