0

My project have a MainForm, i show F_Insert and set MdiParent for MainForm

F_Insert f = new F_Insert();
f.MdiParent = this;  
f.Show();

In F_Insert, i put a button with CLick event like this

 private void btn_Add_Click(object sender, EventArgs e)
 {
       //Insert data to SQL
 }

Besides, i want to auto upload data that inserted from F_Insert every 5 second

I use System.Timer.Timer and set it to Thread in MainForm_Load

Thread t1 = new Thread(new ThreadStart(Timerss)); //In MainFormLoad event
t1.Start(); 

public void Timerss()
    {
        System.Timers.Timer timer = new System.Timers.Timer(5000);
        timer.Elapsed += Timer_Insert_Tick;
        timer.AutoReset = true;
        timer.Start();
    }

private static void Timer_Insert_Tick(object sender, System.Timers.ElapsedEventArgs e)
    {
      //code auto upload data to server here
      //Data get from Sql Local to upload SQL in Server
    }

The problem is it's not working good. I feel when i insert data form F_Insert, data is affected by Timerss thread that i start in MainForm load.

The simple way to show you my problem: when i split two work (Insert and upload) into 2 difference work, it working good, it's mean i'm insert data complete and then, i upload data, it will working good. But when i insert data and data auto upload by timer in the same time, i see some error that: conection sql close or open error, no data get from F_Insert, sometime it get duplicate data (old data)

Please suggeted me some idea for this problem. Sorry but i'm newbie in thread. Thank you !!!

XCode2015
  • 193
  • 1
  • 2
  • 10
  • What's not working? How do you know it's not? What error? etc... – Enigmativity Oct 09 '15 at 06:47
  • 1
    One problem I see here (it's not clear to me what your actual problem is) is that the only reference to the timer is in a local variable. I believe that means that it can and will be garbage collected. – Damien_The_Unbeliever Oct 09 '15 at 06:48
  • Hi @Enigmativity, thanks for watching! The simple way to show you my problem: when i split two work (Insert and upload) into 2 difference work, it working good, it's mean i'm insert data complete and then, i upload data, it will working good. But when i insert data and data auto upload by timer in the same time, i see some error that: conection sql close or open error, no data get from F_Insert, sometime it get duplicate data (old data) ... – XCode2015 Oct 09 '15 at 06:54
  • Hi @Damien_The_Unbeliever, i'm interest in your awser, but i don't understand your awser. please show me more about it! Thanks you! – XCode2015 Oct 09 '15 at 07:01
  • If I were in FCL team, I'd place `[Obsolete("NEVER CREATE THREADS DIRECTLY UNLESS YOU KNOW EXACTLY WHAT ARE YOU DOING")]` on `Thread` constructor. What is the reason to not to use TPL or `async`/`await`? Do you on .NET 3.5? – Dennis Oct 09 '15 at 07:03
  • Hi Dennis, i'm on 4.0 – XCode2015 Oct 09 '15 at 07:06
  • Maybe [this article](https://web.archive.org/web/20150329101415/https://msdn.microsoft.com/en-us/magazine/cc164015.aspx) about timers may help. – Oliver Oct 09 '15 at 07:08
  • Hi @Oliver, i have used Threading.timer and Window.Timer, but finally, for me, System.timer is the best way – XCode2015 Oct 09 '15 at 07:11
  • Hi everyone, idea for my problem that i run another application with the same medthod when i load MainForm (But it is not the best way), is this ok ??? – XCode2015 Oct 09 '15 at 07:36
  • @Damien_The_Unbeliever I believe it's not be GC collected, but the reference to it is lost. (Same thing as it with Tasks, when one is started without reference and method exits but the Task keeps running and is not collected by GC until it's completed). – Fabjan Oct 09 '15 at 07:44
  • The Timer implementation is fragile. The actual errors come form the Sql code you replaced with comments here. So, not really answerable. – H H Oct 09 '15 at 08:26
  • Hi @HenkHolterman, but when i insert complete and upload it step by step. It's working good. The problem occurs when i insert data and Timer is auto upload data – XCode2015 Oct 09 '15 at 08:32
  • So your SQL related code is not thread-safe. – H H Oct 09 '15 at 08:38

1 Answers1

0

Well depending on what you trying to do this code should be modified but i hope it'll give you starting point to work with.

First of all let's create static field:

static volatile bool isDataChanged;

Keyword volatile makes this bool thread-safe, it means that this field always holds latest (and therefore correct) value when it is accessed by any thread in multi-thread environment).

We need this field to hold bool value that is used later to check whether the data was modified or not.

Assuming that data is modified inside click event handler, we should set this flag to true :

 private void btn_Add_Click(object sender, EventArgs e)
 {
       // Data is modified in UI thread

       isDataChanged = true;
 }

Then let's assume that in Timer tick event we should upload the latest data to database (data is located in UI thread and could change in time span in between two tick events).

First of all we check if there is any changes to our data and if there's not we just exits the method. If changes was done we need to upload them to DB and in order to do so we have to deal with the fact that data in Timer thread could very well not be the same as data in our UI thread.

Let's create local variable that will hold correct data that we fetch from UI thread and use this.Invoke() to invoke Func<object> delegate on UI thread. The method that is attached to delegate returns instance of correct data retrieved from UI thread as object. We cast it explicitly to the type that our data is (usually it's one of collection types like List<T> or Dictionary<T1, T2>) and use this data to upload it to the DB.

After that, because our data in DB is the correct one, we change flag isDataChanged to false.

private void Timer_Insert_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
   if(!isDataChanged) return;

   // A very important line. It gets data from UI thread before uploading it
   // Change DataType with your data Type and dataToUpload with data instance

   DataType data = (DataType)this.Invoke(new Func<object>(() => dataToUpload));

   //use data to upload your data to server

   isDataChanged = false;
}

P.S. Also it is better to place reference to our Timer in outer scope (so it can be accessed from anywhere inside the form)

public partial class MyForm : Form
{
   ...
   System.Timers.Timer timer; 

   public void Timerss()
   {
      timer = new System.Timers.Timer(5000); 
   }

   ...
}
Fabjan
  • 13,506
  • 4
  • 25
  • 52
  • Thank you, it's look my problem. I'll try your way. Thank you so much – XCode2015 Oct 09 '15 at 08:16
  • @XCode2015 it should help you with multi-threading but if it's something wrong with database, with sql query, or data processing - it's whole different story – Fabjan Oct 09 '15 at 08:35