Ok, so my app basically checks multiple mail accounts for incoming messages every 5 minutes.

I know about threads but have never coded a multi threaded application. What's the best way to go about this?

Start the thread, have it call an instance method to check for mail, and then let it sleep for 5 minutes? Basically call it within an infinate loop?

Recommended Answers

All 3 Replies

Familiarize yourself with the System.Threading.Interlock and System.Threading.Monitor namespaces for threadsafe best practices. Here is a stub class for a mail checker. You pretty much just need to complete the CheckMail() method for downloading the messages. Depending on the behavior you want the class is plumbed to fire events either asynchronously or synchronously:

using System;
using System.Net.Mail;
using System.Runtime.Remoting.Messaging;
using System.Timers;
using Timer = System.Timers.Timer;

namespace daniweb
{
  public class EmailAuthInfo
  {
    public string EmailAddress { get; set; }
    public string Server { get; set; }
    public string User { get; set; }
    public string Password { get; set; }
    public EmailAuthInfo()
    {
    }
    public EmailAuthInfo(string EmailAddress, string Server, string User, string Password)
      : this()
    {
      this.EmailAddress = EmailAddress;
      this.Server = Server;
      this.User = User;
      this.Password = Password;
    }
  }
  public delegate void MessageReceivedEventHandler(object sender, MessageReceivedEventArgs e);
  public class MessageReceivedEventArgs : EventArgs
  {
    public MailMessage EmailMessage;
    public MessageReceivedEventArgs()
    {
    }
    public MessageReceivedEventArgs(MailMessage EmailMessage)
      : this()
    {
      if (EmailMessage == null)
        throw new ArgumentNullException("EmailMessage");
      this.EmailMessage = EmailMessage;
    }
  }

  public class MailChecker : IDisposable
  {
    public const int DEFAULT_INTERVAL = 1000 * 60 * 5; //5 minutes

    private int syncPoint;
    private Timer timer;
    private EmailAuthInfo auth;
    public event MessageReceivedEventHandler OnMessageReceived;
    private volatile bool stopping;

    private MailChecker()
    {
      syncPoint = 0;
      timer = new Timer(DEFAULT_INTERVAL);
      timer.AutoReset = true;
      timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
      stopping = false;
    }

    public MailChecker(EmailAuthInfo auth)
      : this()
    {
      if (auth == null)
        throw new ArgumentNullException("auth");
      this.auth = auth;
    }
    public MailChecker(EmailAuthInfo auth, int interval)
      : this(auth)
    {
      timer.Interval = interval;
    }

    public void Start()
    {
      if (timer.Enabled)
        throw new InvalidOperationException("Timer is already running");
      timer.Start();
      stopping = false;
      //Fire off our mail checker manually to force immediate retrieval
      new Action(CheckMail).BeginInvoke(null, null); 
    }
    public void Stop()
    {
      stopping = true;
      timer.Stop();
    }


    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        CheckMail();
    }
    private void CheckMail()
    {
      int sync = System.Threading.Interlocked.CompareExchange(ref syncPoint, 1, 0);
      if (sync != 0) //Still running from last timer elapsed call
        return;
      try
      {
        //However you check your email would go here
        for (int i1 = 0; i1 < 5; i1++)
        {
          if (stopping) //You want to support cancelling
            return;
          MailMessage msg = new MailMessage();
          msg.Subject = Guid.NewGuid().ToString();
          
          //You can have the events fired either async or sync
          //FireMessageReceivedSynchronous(msg);
          FireMessageReceivedAsynchronous(msg);
        }
      }
      finally
      {
        //All finished up
        System.Threading.Interlocked.Exchange(ref syncPoint, 0);
      }
    }
    private void FireMessageReceivedSynchronous(MailMessage msg)
    {
      using (msg) //Dispose message after firing event
      {
        var del = this.OnMessageReceived;
        if (del != null)
        {
          del(this, new MessageReceivedEventArgs(msg));
        }
      }
    }
    private void FireMessageReceivedAsynchronous(MailMessage msg)
    {
      var del = this.OnMessageReceived;
      if (del != null)
      {
        //Dispose message in a callback since we're firing async
        del.BeginInvoke(this, new MessageReceivedEventArgs(msg), new AsyncCallback(callback), msg);
      }
      else
      {
        msg.Dispose();
      }
    }
    private static void callback(IAsyncResult ar)
    {
      AsyncResult result = (AsyncResult)ar;
      MessageReceivedEventHandler del = (MessageReceivedEventHandler)result.AsyncDelegate;
      MailMessage msg = (MailMessage)ar.AsyncState;
      try
      {
        del.EndInvoke(ar);
      }
      finally
      {
        msg.Dispose();
      }
    }

    #region IDisposable Members
    public void Dispose()
    {
      stopping = true;
      if (timer != null)
      {
        timer.Dispose();
        timer = null;
      }
    }
    #endregion
  }
}

Calling it:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace daniweb
{
  public partial class frmMailChecker : Form
  {
    private Dictionary<string, MailChecker> clients;

    public frmMailChecker()
    {
      InitializeComponent();
      clients = new Dictionary<string, MailChecker>(StringComparer.CurrentCultureIgnoreCase);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      EmailAuthInfo info = new EmailAuthInfo("sk@sk.com", "server", "user", "pass");
      MailChecker checker = new MailChecker(info, 1000 * 10); //10s for testing
      clients.Add(info.EmailAddress, checker);
      checker.OnMessageReceived += new MessageReceivedEventHandler(checker_OnMessageReceived);
      checker.Start();
    }

    void checker_OnMessageReceived(object sender, MessageReceivedEventArgs e)
    {
      Console.WriteLine("Message received: " + e.EmailMessage.Subject);
    }
  }
}

Wow. That has lost me. I'm going to spend some time analyzing that code to make heads and tails of it.

It's mainly because I've no idea what IDisposal means etc. I'll use google.

Edit: On a recent Google search I found IDisposal to be an Interface that releases umanaged resources. So any resources allocated within the object is automatically released by the garbage collector. I thought C# did that anyway.

I understand design patterns though, I came from PHP, so this is a little more advanced.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.