проблема таймера в потоке - PullRequest
2 голосов
/ 02 июня 2011

У меня есть приложение, в котором в три datagridview независимо в три потока загружают данные из службы wcf. Я выполняю в каждом потоке таймер, который каждую секунду загружает эти данные.

Моя проблема в том, что каждый раз, когда мой поток перебрасывает каждый поток, но только так, как я показываю в методе timerNowyYork_Elapsed

Есть идеи, почему это происходит? Я плохо блокирую нить?

этот код:

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

namespace Sprawdzanie_warunków_pogodowych
{
public partial class Form1 : Form
{
    PogodaEntities entity = new PogodaEntities();
    System.Timers.Timer timerKrakow = new System.Timers.Timer();
    System.Timers.Timer timerSzczecin = new System.Timers.Timer();
    System.Timers.Timer timerNowyYork = new System.Timers.Timer();
    KeyValuePair<string, string> krakowInfo;
    KeyValuePair<string, string> szczecinInfo;
    KeyValuePair<string, string> nowyYorkInfo;

    public Form1()
    {
        System.Net.ServicePointManager.Expect100Continue = false;
        InitializeComponent();
        List<MiastoContainer> miasta = (from miasto in entity.Miasta
                                        select new MiastoContainer()
                                   {
                                       MiastoName = miasto.Nazwa,
                                       Panstwo = miasto.Państwo
                                   }).ToList();
        krakowInfo = new KeyValuePair<string, string>(miasta[0].MiastoName, miasta[0].Panstwo);
        szczecinInfo = new KeyValuePair<string, string>(miasta[1].MiastoName, miasta[1].Panstwo);
        nowyYorkInfo = new KeyValuePair<string, string>(miasta[2].MiastoName, miasta[2].Panstwo);

        ParameterizedThreadStart ptsKrakow = new ParameterizedThreadStart(PobierzKrakow);
        Thread tKrakow = new Thread(ptsKrakow);
        tKrakow.Start(this.dataGridViewKrakow);

        ParameterizedThreadStart ptsSzczecin = new ParameterizedThreadStart(PobierzSzczecin);
        Thread tSzczecin = new Thread(ptsSzczecin);
        tSzczecin.Start(this.dataGridViewSzczecin);
    }

    private void oAutorzeToolStripMenuItem_Click(object sender, EventArgs e)
    {
        new AboutBox1().Show();
    }

    private void zapiszRaportToolStripMenuItem_Click(object sender, EventArgs e)
    {

    }

    public void PobierzKrakow(object parameters)
    {
        this.timerKrakow.Elapsed += new System.Timers.ElapsedEventHandler(timerKrakow_Elapsed);
        this.timerKrakow.Enabled = true;
        this.timerKrakow.Interval = 1000;
        this.timerKrakow.Start();
    }

    public void PobierzSzczecin(object parameters)
    {
        this.timerSzczecin.Elapsed += new System.Timers.ElapsedEventHandler(timerSzczecin_Elapsed);
        this.timerSzczecin.Enabled = true;
        this.timerSzczecin.Interval = 1000;
        this.timerSzczecin.Start();
    }

    public void PobierzNowyYork(object parameters)
    {
        this.timerNowyYork.Elapsed += new System.Timers.ElapsedEventHandler(timerNowyYork_Elapsed);
        this.timerNowyYork.Enabled = true;
        this.timerNowyYork.Interval = 1000;
        this.timerNowyYork.Start();
    }

    void timerNowyYork_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {  GlobalWeather.Weather weather = new GlobalWeather.Weather();
        lock (weather)
        {
            //thread always start from here
            List<object> weatherList = new List<object>();
            weatherList.Add(weather.GetTempreature(nowyYorkInfo.Key, nowyYorkInfo.Value));
           //and end here , never come any line further
            weatherList.Add(weather.GetPressure(nowyYorkInfo.Key, nowyYorkInfo.Value));
            weatherList.Add(weather.GetHumidity(nowyYorkInfo.Key, nowyYorkInfo.Value));
            weatherList.Add(weather.GetVisibility(nowyYorkInfo.Key, nowyYorkInfo.Value));
            entity.SaveChanges();

            WarunkiPogodowe warunki = new WarunkiPogodowe()
            {
                Temperatura = weatherList[0].ToString(),
                Ciśnienie = weatherList[1].ToString(),
                Wilgotność = weatherList[2].ToString(),
                Widoczność = weatherList[3].ToString(),
                DataSprawdzenia = DateTime.Now
            };
            entity.AddToWarunkiPogodowe(warunki);
            entity.SaveChanges();
            int miastoId = entity.Miasta.First(m => m.Nazwa == nowyYorkInfo.Key).id;
            Miasto_has_WarunkiPogodowe m_has_wp = new Miasto_has_WarunkiPogodowe()
            {
                idMiasto_FK = miastoId,
                idWarunkiPogodowe_FK = warunki.id
            };
            entity.AddToMiasto_has_WarunkiPogodowe(m_has_wp);
            entity.SaveChanges();

            this.dataGridViewNowyYork.Rows.Add(warunki);
        }
    }

    void timerSzczecin_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        GlobalWeather.Weather weather = new GlobalWeather.Weather();

        lock (weather)
        {
            List<object> weatherList = new List<object>();
            weatherList.Add(weather.GetTempreature(szczecinInfo.Key, szczecinInfo.Value));
            weatherList.Add(weather.GetPressure(szczecinInfo.Key, szczecinInfo.Value));
            weatherList.Add(weather.GetHumidity(szczecinInfo.Key, szczecinInfo.Value));
            weatherList.Add(weather.GetVisibility(szczecinInfo.Key, szczecinInfo.Value));
            entity.SaveChanges();

            WarunkiPogodowe warunki = new WarunkiPogodowe()
            {
                Temperatura = weatherList[0].ToString(),
                Ciśnienie = weatherList[1].ToString(),
                Wilgotność = weatherList[2].ToString(),
                Widoczność = weatherList[3].ToString(),
                DataSprawdzenia = DateTime.Now
            };
            entity.AddToWarunkiPogodowe(warunki);
            entity.SaveChanges();
            int miastoId = entity.Miasta.First(m => m.Nazwa == szczecinInfo.Key).id;
            Miasto_has_WarunkiPogodowe m_has_wp = new Miasto_has_WarunkiPogodowe()
            {
                idMiasto_FK = miastoId,
                idWarunkiPogodowe_FK = warunki.id
            };
            entity.AddToMiasto_has_WarunkiPogodowe(m_has_wp);
            entity.SaveChanges();

            this.dataGridViewSzczecin.Rows.Add(warunki);
        }
    }
    void timerKrakow_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        GlobalWeather.Weather weather = new GlobalWeather.Weather();

        lock (weather)
        {
            List<object> weatherList = new List<object>();
            weatherList.Add(weather.GetTempreature(krakowInfo.Key, krakowInfo.Value));
            weatherList.Add(weather.GetPressure(krakowInfo.Key, krakowInfo.Value));
            weatherList.Add(weather.GetHumidity(krakowInfo.Key, krakowInfo.Value));
            weatherList.Add(weather.GetVisibility(krakowInfo.Key, krakowInfo.Value));
            entity.SaveChanges();

            WarunkiPogodowe warunki = new WarunkiPogodowe()
            {
                Temperatura = weatherList[0].ToString(),
                Ciśnienie = weatherList[1].ToString(),
                Wilgotność = weatherList[2].ToString(),
                Widoczność = weatherList[3].ToString(),
                DataSprawdzenia = DateTime.Now
            };
            entity.AddToWarunkiPogodowe(warunki);
            entity.SaveChanges();
            int miastoId = entity.Miasta.First(m => m.Nazwa == krakowInfo.Key).id;
            Miasto_has_WarunkiPogodowe m_has_wp = new Miasto_has_WarunkiPogodowe()
            {
                idMiasto_FK = miastoId,
                idWarunkiPogodowe_FK = warunki.id
            };
            entity.AddToMiasto_has_WarunkiPogodowe(m_has_wp);
            entity.SaveChanges();

            this.dataGridViewKrakow.Rows.Add(warunki);
        }
    }
}

class MiastoContainer
{
    string miastoName;

    public string MiastoName
    {
        get { return miastoName; }
        set { miastoName = value; }
    }
    string panstwo;

    public string Panstwo
    {
        get { return panstwo; }
        set { panstwo = value; }
    }

    public MiastoContainer()
    { }

    public MiastoContainer(string miasto, string panstwo)
    {
        this.MiastoName = miasto;
        this.Panstwo = panstwo;
    }

    public void Add(MiastoContainer item)
    {
        ((ICollection<MiastoContainer>)this).Add(item);
    }

}

}

Ответы [ 4 ]

2 голосов
/ 02 июня 2011

System.Timers.Timer позволяет вам установить SynchronizingObject, чтобы он вызывал обратный вызов в потоке пользовательского интерфейса.Когда вы создаете свои таймеры, напишите:

this.timerKrakow.SynchronizingObject = this;

Событие истекшего времени будет вызвано в потоке пользовательского интерфейса.Это исключает необходимость блокировок в ваших обработчиках событий.

Кстати, вы можете сделать то же самое с System.Windows.Forms.Timer, который всегда вызывает обработчик событий в потоке пользовательского интерфейса.

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

Если вы решите не делать этого в потоке пользовательского интерфейса, вам необходимо выполнить синхронизациюдоступ к интерфейсу.Обработчик событий таймера не может просто изменять элементы пользовательского интерфейса.Вместо этого вам нужно вызвать this.Invoke , чтобы любая модификация пользовательского интерфейса выполнялась в потоке пользовательского интерфейса.

Я настоятельно рекомендую вам НЕ использовать System.Timers.Timer.В документации указано:

Компонент Timer перехватывает и подавляет все исключения, выдаваемые обработчиками событий для события Elapsed.Это поведение может быть изменено в будущих выпусках .NET Framework.

Другими словами, если в вашем обработчике событий есть ошибка, которая выдает исключение, вы никогда об этом не узнаете.Я предлагаю использовать System.Windows.Forms.Timer или System.Threading.Timer вместо.

2 голосов
/ 02 июня 2011

Ваши замки совершенно бесполезны.Поскольку вы блокируете только что созданный объект, каждая блокировка будет иметь свой собственный идентификатор и не будет влиять друг на друга вообще.

Вам нужны все блокировки, которые должны исключать друг друга, чтобы использовать один и тот же объект в качестве идентификатора..

0 голосов
/ 02 июня 2011

Мне кажется, что вы обращаетесь к DataGridView напрямую из другого потока. Вы не должны этого делать. Элементы управления пользовательского интерфейса всегда должны вызываться из потока пользовательского интерфейса. Вы можете использовать интерфейс ISynchronizeInvoke для передачи данных в правильный поток.

this.dataGridViewNowyYork.Invoke(new Action(() => {
    this.dataGridViewNowyYork.Rows.Add(warunki);
}), null);
0 голосов
/ 02 июня 2011

Я не совсем понимаю ваш вопрос, но (если я не ошибаюсь) обратные вызовы таймера происходят в ThreadPool (или в потоке GUI, в зависимости от использования), поэтому запускать их в разных потоках бессмысленно.

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