Diskuze: použití System.Timers.Timer

C# .NET .NET (C# a Visual Basic) použití System.Timers.Timer American English version English version

Avatar
marek.lubos
Člen
Avatar
marek.lubos:

Ahojte kolegové,
dělám takový menší program a budu tam potřeboval použít Timer. Vím, jak se cca používá, ale nejsem si jist, jestli ho používám úplně správně (jestli není lepší ho použít nějak jinak), nebo jestli na tuto věc není lepší použít něco jiného???

Mám funkci, která by měla načíst hodnoty z DB, pokud se to nepovede, z nějakého důvodu (nefunguje síť, výpadek...) chci aby se to zkoušelo třeba každou minutu znova ... je takto ten Timer použitelný, nebo to nedává smysl?

public Setting LoadSettings()
{
    System.Timers.Timer timer = new System.Timers.Timer();
    timer.AutoReset = true;
    timer.Interval = 60000;

    string pc_name = System.Environment.MachineName;
    string sql = "SELECT pc_name, account, lop_prg_path, lop_detect_path, lop_param, sam_prg_path, sam_detect_path, active, last_detect, period_scan, update_interval FROM oez.ScanSettings WHERE pc_name=@pc_name";
    SqlConnection con = new SqlConnection(_connection);
    SqlCommand cmd = new SqlCommand(sql, con);
    SqlDataReader row;
    Setting set = new Setting();
    try
    {
        cmd.Parameters.AddWithValue("@pc_name", pc_name);
        con.Open();
        row = cmd.ExecuteReader();
        while (row.Read())
        {
            set.pc_name = row["pc_name"].ToString();
            set.account = row["account"].ToString();
            set.lop_prg_path = row["lop_prg_path"].ToString();
            set.lop_detect_path = row["lop_detect_path"].ToString();
            set.lop_param = row["lop_param"].ToString();
            set.sam_prg_path = row["sam_prg_path"].ToString();
            set.sam_detect_path = row["sam_detect_path"].ToString();
            set.active = row.GetInt32(row.GetOrdinal("active"));
            set.last_detect = row.GetDateTime(row.GetOrdinal("last_detect"));
            set.period_scan = row.GetInt32(row.GetOrdinal("period_scan"));
            set.update_interval = row.GetInt32(row.GetOrdinal("update_interval"));
        }
    }
    catch (Exception ex)
    {
        timer.Enabled = true;
        timer.Start();
        timer.Elapsed += new System.Timers.ElapsedEventHandler(TryDbAgain);
    }
    finally
    {
        if (timer.Enabled == true)
        {
            timer.Close();
            timer.Dispose();
        }
    }

    return set;
}
Editováno 6.2.2015 20:06
 
Odpovědět 6.2.2015 20:03
Avatar
marek.lubos
Člen
Avatar
marek.lubos:

ještě jsem zapomněl dodat metodu TryDbAgain(), která jenom opět zavolá LoadSetting, je to takto správně?

private void TryDbAgain(object sender, EventArgs e)
      {
          LoadSettings();
      }
 
Nahoru Odpovědět 6.2.2015 20:08
Avatar
Odpovídá na marek.lubos
sadlomaslox25:

obavam se ze to fungovat nebude. poradi provadeni je try-catch-finally to znamena ze v try si udelas timer, v catch na vyjimce ho spustis a hned na to ho ve finally zrusis takze se nikdy neprovede.

dalsi nedostatky bych videl v nepouzivani bloku using pro connection, command a datareader. taky je problem v poradi timer.start a timer.elapsed. v tomhle konkretnim pripade to bude fungovat ale v pripade mensiho casu uz to nemusi nutne fungovat. a taky timer.enable=true je to same jak timer.start

 
Nahoru Odpovědět 7.2.2015 1:52
Avatar
marek.lubos
Člen
Avatar
Odpovídá na sadlomaslox25
marek.lubos:

Díky za komentář :)

  • using zapracuju, už se ho snažím zapracovávat, bohužel na něj nejsem zvyklý, tak jsem na něj zapomněl :) moc díky za připomenutí
  • prohození timer.start a timer.elapsed, tak jsem si toho vyšiml až pozdě, že je to prohozeno, ale už mně to nešlo z editovat :(
  • mohl by jsi mně prosím naznačit, jak by to mělo vypadat správně? V bloku finally ten timer zastavuju, protože jsem nevěděl, kde jinde a nechtěl jsem aby běžel nějak donekonečna. Myslel jsem, že to funguje tak, že, tak začnu blokem try, tam dojde k chybě, tak se přesunu do bloku catch, tam se počká danou dobu (v našem případě jednu minutu) a po uplynutí této doby se rovnou přesunu do TryDbAgain(), ze které jdu do LoadSettings(), tentokrát se provede vše OK, tak se dostanu do bloku finally, kde timer zruším :)

ale asi to je tedy špatně, myslíš, že by jsi mně mohl prosím naznačit lepší cestu?

Předem děkuji

Editováno 7.2.2015 11:53
 
Nahoru Odpovědět 7.2.2015 11:52
Avatar
Odpovídá na marek.lubos
sadlomaslox25:

jo a taky sem zapomel ze to tvoje vubec nebude fungovat pres ten casovac protoze ty na casovaci volas TryDbAgain ktera vola LoadSettings ktera ti vraci ty settings. Ale pri volani z toho casovace nic nedelas s tema hodnotama. Taky je dobre si uvedomit, ze zrovna tenhle casovac neni UI, takze v obsluze toho casovace nemuzes manipulovat s UI prvky (nemuzes nic delas s labelama,tlacitkama atd.). Ja osobne bych pouzil tuhle konstrukci

private void StartMethod()
{
    Retry(() =>
    {
        //co je v tomhle bloku se provede na jinem vlakne
        var data = GetDataFromDB();
        label1.Invoke(new Action(() => label1.Text = data.ToString())); //co je volane v invoke to se provede na UI vlakne
    }, 5, TimeSpan.FromSeconds(1));
}

public object GetDataFromDB()
{
    //zde je originalni kod pro nacteni dat
    return null;
}

private void Retry(Action what, int retryCount, TimeSpan delay)
{
    Task.Factory.StartNew(() =>  //vytvori nove vlakno
    {
        for (int i = 0; i < retryCount; i++)
        {
            try
            {
                what(); //na tom novem vlakne skusi provest originalni akci
                return;
            }
            catch (Exception ex)
            {
                Thread.Sleep(delay); //kdyz se ta originalni akce nepovede uspi se to a zkusi znova doku jede hlavní cyklus
            }
        }
    });
}

snazil sem se najit balanc mezi jednoduchou implementaci a pouzitelnosti.

 
Nahoru Odpovědět 7.2.2015 18:40
Avatar
marek.lubos
Člen
Avatar
marek.lubos:

tyjo, tak to je už na mě moc brutální :( ať na to koukám, jak koukám, tak to stjně tak nějak nemá hlavu a patu :( UI nepotřebuji, protože se snažím vytvořit službu, která poběží na pozadí systému, která UI stejně nemá :( myslíš, že by jsi mně pomohl ten timer napasovat na ten kód co mám, aby mně to dávalo alespoň trochu smysl??? V C# se po kouskách pousunuju dopředu a dopředu, ale toto je na mě až moc velká dávka :(

Editováno 7.2.2015 19:30
 
Nahoru Odpovědět 7.2.2015 19:29
Avatar
Odpovídá na marek.lubos
Michael Škrášek:

Udělal bych to takto, radši než přes úlohy přes vlákna, podporují starší framework ;), je to podobné timeru, jen lepší

private void nacteni_nastaveni()
        {
            string pc_name = System.Environment.MachineName;
            string sql = "SELECT pc_name, account, lop_prg_path, lop_detect_path, lop_param, sam_prg_path, sam_detect_path, active, last_detect, period_scan, update_interval FROM oez.ScanSettings WHERE pc_name=@pc_name";
            SqlConnection con = new SqlConnection(_connection);
            SqlCommand cmd = new SqlCommand(sql, con);
            SqlDataReader row;
            Setting set = new Setting();
            try
            {
                cmd.Parameters.AddWithValue("@pc_name", pc_name);
                con.Open();
                row = cmd.ExecuteReader();
                while (row.Read())
                {
                    set.pc_name = row["pc_name"].ToString();
                    set.account = row["account"].ToString();
                    set.lop_prg_path = row["lop_prg_path"].ToString();
                    set.lop_detect_path = row["lop_detect_path"].ToString();
                    set.lop_param = row["lop_param"].ToString();
                    set.sam_prg_path = row["sam_prg_path"].ToString();
                    set.sam_detect_path = row["sam_detect_path"].ToString();
                    set.active = row.GetInt32(row.GetOrdinal("active"));
                    set.last_detect = row.GetDateTime(row.GetOrdinal("last_detect"));
                    set.period_scan = row.GetInt32(row.GetOrdinal("period_scan"));
                    set.update_interval = row.GetInt32(row.GetOrdinal("update_interval"));
                }
            }
            catch (Exception ex)
            {
                neslo_se_pripojit();
            }
        }

        private bool zkouska_pripojeni()
        {
            try
            {
                //zde se pokusis pripojit
                return true;
            }
            catch
            {
                return false;
            }
        }

        private void neslo_se_pripojit()
        {
            Thread t = new Thread(timer);
            t.Start();
        }

        private void timer()
        {
            bool p = true;
            while (p)
            {
                p = zkouska_pripojeni();
                Thread.Sleep(500);
            }
        }
Nahoru Odpovědět 7.2.2015 19:43
Proč to dělat složitě, když to jde jednoduše.
Avatar
Odpovídá na marek.lubos
Luboš Běhounek (Satik):

Pokud by ta aplikace nedělala v tu chvíli nic jiného, tak stačí vlákno uspat a timer ani nic jiného nemusíš vůbec řešit :) .

Nahoru Odpovědět 7.2.2015 19:43
:)
Avatar
Odpovídá na marek.lubos
sadlomaslox25:

ok. snazil sem se to trochu osekat.

private void StartMethod()
{
    //tady se neco deje predtim nez chces zavolat tu metodu
    string result = Retry(GetDataFromDB, 5, TimeSpan.FromSeconds(1)) as string;
    //tady zpracujes ten vysledek
}

public string GetDataFromDB()
{
    //zde je originalni kod pro nacteni dat
    return null;
}

private object Retry(Func<object> what, int retryCount, TimeSpan delay)
{
    for (int i = 0; i < retryCount; i++)
    {
        try
        {
            return what();
        }
        catch (Exception ex)
        {
            Thread.Sleep(delay); //kdyz se ta originalni akce nepovede uspi se to a zkusi znova doku jede hlavní cyklus
        }
    }
    return null; //budto vrati null kdyz vyprsi pokusy a nepodari se ziskat data
    // nebo throw new Exception(); abys to zhavaroval
}
 
Nahoru Odpovědět 7.2.2015 20:11
Avatar
Jan Vargovský
Redaktor
Avatar
Jan Vargovský:

To nestačí něco takhle jednoduchého?

new Thread(() =>
{
    while (true)
    {
        var data = GetDataFromDB();
        if (data == null)
            Thread.Sleep(TimeSpan.FromMinutes(1));
        else
            break;
    }

}).Start();

nebo jestli tam vyhazuješ vyjímku

new Thread(() =>
{
    bool success = false;
    do
    {
        try
        {
            var data = GetDataFromDB();
            success = true;
        }
        catch (Exception ex)
        {
            Thread.Sleep(TimeSpan.FromMinutes(1));
        }
    } while (!success);
}).Start();
 
Nahoru Odpovědět 7.2.2015 21:33
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 10.