Hello,
I am writing a multi-threaded C# console program that
is executed by a windows Service which acts as a Service Control
Manager
for the console app and thereby executes the application
as a Service.
For the Service to run the apps as a Service, they have to be written
a certain way.
The Service works by starting up a process for the application, before
executing the app code.
On entering the main method of the app, it tests for a Mutex and as
long as there is already a mutex when the service was started, the
conditions will make the app run endlessly unless the controlling
service is stopped.
The main issue is that I have been having some unexpected results in
the execution of the app. There are 2 threads running concurrently but
sometimes it seems like one thread is smothered or deprived of
resources for execution.
Here is the source code.
namespace BulkSmsClient
{
class Program
{
static Mutex mSingleton;
private static readonly ILog log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static Thread T1;
public static Thread T2;
private static object gate = new object();
private static object gate2 = new object();
//BELOW THE VARIABLE FOR MANAGING THIS AS SERVICE USING
PROCESS MANAGER (VAS SYSTEM MANAGER)// REMOVE IF NOT NEEDED
private static bool m_isApplicationTerminating;
//END
static void Main(string[] args)
{
try
{
//CAUTION: UNCOMMENT THE MUTEX TEST CODE BLOCK BELOW
IF YOU ARE NOT USING THE PROCESS MANAGER SERVICE.
//Check if there is already an instance of the
application running
//Kill second instance if true
//REQUIRES reference to the System.Threading namespace
//bool created;
//mSingleton = new Mutex(true, "BulkSmsClient", out
created);
//if (!created)
//{
// Console.WriteLine("Already running, exiting");
// return;
//}
//Configure log4net passing in the log4net.xml config
file at the Application root folder.
XmlConfigurator.Configure(new
System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location.Remove(System.Reflection.Assembly.GetExecutingAssembly().Location.LastIndexOf("\
\BulkSmsClient.exe")) + "\\log4net.xml"));
//// log4net configuration in the app.config file here
for configuring this to log to the PROCESS MANAGER's Global log file
//string appId = String.Format(@"{0}\{1}.exe",
Environment.CurrentDirectory,
//
Assembly.GetExecutingAssembly().GetName().Name);
//XmlConfigurator.Configure();
//log4net.GlobalContext.Properties["identity"] =
appId;
log.Info("Entering Application...");
log.Debug("Initialising ServicePointManager to use the
Certificate Policy...");
//DEPRECATED: Get the ServicePointManager to use the
Certificate Policy. This allows you to use HTTPS with self-signed
certificates
//System.Net.ServicePointManager.CertificatePolicy =
new TrustAllCertificatePolicy();
// CURRENT: allows for validation of SSL conversations
//Requires the following imports:
//using System.Net.Security;
//using System.Security.Cryptography.X509Certificates;
//and the Callback method below:
ValidateRemoteCertificate
//TODO: Separate into a Class
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
new RemoteCertificateValidationCallback(
ValidateRemoteCertificate
);
//Retrieving the AppSettings Section of the
Configuration file
NameValueCollection nvcAllAppSettings =
ConfigurationManager.AppSettings;
//BELOW THE VARIABLES FOR MANAGING THIS AS SERVICE
USING PROCESS MANAGER (VAS SYSTEM MANAGER)// REMOVE IF NOT NEEDED
Mutex mutex = null;
bool isFirstInstance = true;
m_isApplicationTerminating = false;
//END
log.Debug("Enter Main method");
log.Debug(string.Format("Main Thread Id {0}",
Thread.CurrentThread.ManagedThreadId.ToString()));
T1 = new Thread(new
ParameterizedThreadStart(sendMessage));
T2 = new Thread(new
ParameterizedThreadStart(updateDeliveryReportsDB));
T1.Start(nvcAllAppSettings);
T2.Start(nvcAllAppSettings);
// Continue whilst there are active threads
while (!m_isApplicationTerminating)
{
if (!m_isApplicationTerminating)
{
//mutex = new Mutex(false,
Environment.CurrentDirectory, out isFirstInstance);
string mutexname =
String.Format(@"{0}\{1}.exe", Environment.CurrentDirectory,
Assembly.GetExecutingAssembly().GetName().Name);
mutex = new Mutex(false,
mutexname.Replace(@"\", String.Empty), out isFirstInstance);
}
if (isFirstInstance)
{
m_isApplicationTerminating = true;
}
else
{
if (mutex != null)
{
mutex.Close();
}
//Thread.Sleep(1000);
}
}
log.Debug("Exit Main method");
}
catch (Exception ex)
{
log.Error(ex.Message);
}
}
private static void sendMessage(object m_nvcAllAppSettings)
{
while (!m_isApplicationTerminating)
{
log.Debug(string.Format("Inside SendMessage Thread
with Thread Id {0}",
Thread.CurrentThread.ManagedThreadId.ToString()));
NameValueCollection nvcAllAppSettings =
(NameValueCollection)m_nvcAllAppSettings;
//Instantiate Web Service
try
{
int i = 0;
string strVendor = String.Empty;
//DateTime datStartTime;
//DateTime datEndTime;
Service messageController;
ClientMessageResult result;
ClientInfo client = null;
ClientMessage message;
service1 retrieveTransLog;
TransactionLog[] transactionlog;
string[] strGuidResult;
string messageId = string.Empty;
//TransactionScope transaction = null;
//Initialising the A3BulkSmsDeliveryReports
WebService
retrieveTransLog = new service1();
log.Debug("Connecting to host: " +
BulkSmsClient.Properties.Settings.Default.BulkSmsClient_org_dyndns_a3ando_Service);
messageController = new Service();
log.Debug("Connected successfully to host: " +
BulkSmsClient.Properties.Settings.Default.BulkSmsClient_org_dyndns_a3ando_Service);
log.Debug("Reading data from the messaging source
table - Sms2Vendor.");
//Read data from the messaging source table -
Sms2Vendor.
DataTable dtDueSms =
Sms2Vendor.GetDueSms2Vendor();
result = null;
client = new ClientInfo();
message = new ClientMessage();
string strMessageBody = String.Empty;
// My-Id will be replaced by a Client Id supplied
by A3&O
client.ClientId = nvcAllAppSettings["ClientId"];
// My-Key will be replaced by a Client Key
supplied by A3&O
client.ClientKey =
nvcAllAppSettings["Authorization"];
//client.ClientName = @"ADMIN";
client.ClientName =
nvcAllAppSettings["ClientName"];
if (dtDueSms.Rows.Count > 0)
{
for (int m = 0; m < dtDueSms.Rows.Count; m++)
{
//Construct message
strMessageBody = dtDueSms.Rows[m]
["Message"].ToString();
message.Header = (!
string.IsNullOrEmpty(dtDueSms.Rows[m]["SenderId"].ToString())) ?
dtDueSms.Rows[m]["SenderId"].ToString() : nvcAllAppSettings["Header"];
message.Body = strMessageBody;
message.DestinationAddress =
dtDueSms.Rows[m]["PhoneNo"].ToString();
//Set other parameters
strVendor = (!
string.IsNullOrEmpty(dtDueSms.Rows[m]["Vendor"].ToString())) ?
dtDueSms.Rows[m]["Vendor"].ToString() : nvcAllAppSettings["Vendor"];
//Get the time before the Server submit is
done
//string strStartTime =
retrieveTransLog.GetServerDateTime();
//Submit the message. Send it to the phone
and log the transaction.
log.Debug("Sending the sms");
result =
messageController.SubmitMessage(client, message);
log.Debug("Completed Sending the sms.
Status: " + result.Result.ToString());
//Thread.Sleep(5000);
//Extract guid
if (result.Result.ToString().ToUpper() ==
"OK")
{
strGuidResult =
result.Message.ToString().Split(':');
messageId = strGuidResult[2].Trim();
}
else
{
messageId = null;
}
//NEED TO TEST THAT Result returns OK here
or else return from method
if (result != null)
{
if (result.Result.ToString().ToUpper()
== "OK")
{
//Mark this scope as transactional
and starting a transaction
//transaction = new
TransactionScope();
lock (gate)
{
//Update the tables to
indicate that message has been sent but not yet delivered
i =
Sms2Vendor.UpdateSentButUndelivrdSms(dtDueSms.Rows[m]
["AutoID"].ToString(), dtDueSms.Rows[m]["PhoneNo"].ToString(),
dtDueSms.Rows[m]["Message"].ToString(), strVendor, null,
DateTime.Parse(dtDueSms.Rows[m]["DateSent"].ToString()), null, 5,
null, message.Header, messageId, dtDueSms.Rows[m]
["Msg_Type"].ToString());
}
//Commit the transaction
//transaction.Complete();
//Dispose the transaction object
//transaction.Dispose();
}
else
{
//Log the errorneous
lock (gate)
{
//Update the tables to
indicate that message has been sent but not yet delivered
i =
Sms2Vendor.UpdateSentButUndelivrdSms(dtDueSms.Rows[m]
["AutoID"].ToString(), dtDueSms.Rows[m]["PhoneNo"].ToString(),
dtDueSms.Rows[m]["Message"].ToString(), strVendor, null,
DateTime.Parse(dtDueSms.Rows[m]["DateSent"].ToString()), null, 5,
null, message.Header, messageId, dtDueSms.Rows[m]
["Msg_Type"].ToString());
}
log.Debug(result.Result.ToString().ToUpper());
}
}
transactionlog =
retrieveTransLog.GetTransactionLog(client.ClientId, messageId);
if (result != null)
{
if (result.Result.ToString().ToUpper()
== "OK")
{
if (transactionlog.Length > 0)
{
//Update the messaging source
table - Sms2Vendor//Insert into the Report table - VendorSmsReport
if
(transactionlog[0].Status.ToLower().Trim() == "delivrd")
{
log.Debug("Sms was
DELIVERED. Updating the Sms2Vendor and VendorSmsReport tables");
//To add more
transactional support for the two statements below
//Mark this scope as
transactional and starting a transaction
//transaction = new
TransactionScope();
lock (gate)
{
i =
Sms2Vendor.UpdateDelivrdSms(dtDueSms.Rows[m]["AutoID"].ToString(),
message.DestinationAddress, message.Body, strVendor, null,
DateTime.Parse(dtDueSms.Rows[m]["DateSent"].ToString()),
transactionlog[0].Status, 1,
DateTime.Parse(transactionlog[0].DeliveryDateTime), message.Header,
messageId, dtDueSms.Rows[m]["Msg_Type"].ToString());
}
//Commit the transaction
//transaction.Complete();
//End this transaction
scope by disposing the transaction object
//transaction.Dispose();
log.Debug("Update
complete");
}
else
{
log.Debug("Sms NOT YET
DELIVERED. Updating the Sms2Vendor and VendorSmsReport tables");
//Mark this scope as
transactional and starting a transaction
//transaction = new
TransactionScope();
lock (gate)
{
i =
Sms2Vendor.UpdateDelivrdSms(dtDueSms.Rows[m]["AutoID"].ToString(),
message.DestinationAddress, message.Body, strVendor, null,
DateTime.Parse(dtDueSms.Rows[m]["DateSent"].ToString()),
transactionlog[0].Status, 5, null, message.Header, messageId,
dtDueSms.Rows[m]["Msg_Type"].ToString());
}
//Commit the transaction
//transaction.Complete();
//End this transaction
scope by disposing the transaction object
//transaction.Dispose();
log.Debug("Update
complete");
}
}
//Console.WriteLine("OK");
log.Debug("OK");
}
else
{
log.Debug(result.Result.ToString().ToUpper());
}
}
// Sleep after each iteration
Thread.Sleep(1000);
}
}//Comment out the else block below to execute the
Application indefinitely or forever.
else
{
Thread.Sleep(1000);
}
//}
}
catch (Exception ex)
{
log.Error(ex.Message);
}
log.Debug(string.Format("Exiting SendMessage Thread
with Thread Id {0}",
Thread.CurrentThread.ManagedThreadId.ToString()));
}
}
private static void updateDeliveryReportsDB(object
m_nvcAllAppSettings)
{
while (!m_isApplicationTerminating)
{
log.Debug(string.Format("Inside
UpdateDeliveryReportsDB Thread with Thread Id {0}",
Thread.CurrentThread.ManagedThreadId.ToString()));
NameValueCollection nvcAllAppSettings =
(NameValueCollection)m_nvcAllAppSettings;
try
{
//int i = 0;
string strVendor = String.Empty;
//DateTime datStartTime;
//DateTime datEndTime;
//Service messageController;
//ClientMessageResult result;
ClientInfo client = new ClientInfo(); ;
//ClientMessage message;
service1 retrieveTransLog = new service1(); ;
TransactionLog[] transactionlog;
//TransactionScope transaction = null;
//Loop through Database and update Delivery reports
DataTable dtGetTop10UndeliveredSms =
Sms2Vendor.GetTop10UndeliveredSms(nvcAllAppSettings["Vendor"]);
if (dtGetTop10UndeliveredSms.Rows.Count > 0)
{
for (int j = 0; j <
dtGetTop10UndeliveredSms.Rows.Count; j++)
{
if (client == null)
{
client = new ClientInfo();
}
client.ClientId = (!
string.IsNullOrEmpty(client.ClientId)) ? client.ClientId :
nvcAllAppSettings["ClientId"];
if (retrieveTransLog == null)
{
retrieveTransLog = new service1();
}
transactionlog =
retrieveTransLog.GetTransactionLog(client.ClientId,
dtGetTop10UndeliveredSms.Rows[j]["MessageId"].ToString());
//Update the local Database tables
if ((transactionlog.Length > 0) &&
(transactionlog[0].Status.ToLower().Trim() == "delivrd"))
{
//We have a match
//Update the Sms2Vendor table
log.Debug("Sms was DELIVERED. Updating
the Sms2Vendor and VendorSmsReport tables");
//Mark this scope as transactional and
starting a transaction
//transaction = new
TransactionScope();
lock (gate2)
{
int k =
Sms2Vendor.UpdateDelivrdSms(dtGetTop10UndeliveredSms.Rows[j]
["AutoID"].ToString(), dtGetTop10UndeliveredSms.Rows[j]
["PhoneNo"].ToString(), dtGetTop10UndeliveredSms.Rows[j]
["Message"].ToString(), dtGetTop10UndeliveredSms.Rows[j]
["Vendor"].ToString(), null,
DateTime.Parse(dtGetTop10UndeliveredSms.Rows[j]
["DateSent"].ToString()), transactionlog[0].Status, 1,
DateTime.Parse(transactionlog[0].DeliveryDateTime),
dtGetTop10UndeliveredSms.Rows[j]["SenderId"].ToString(),
dtGetTop10UndeliveredSms.Rows[j]["MessageId"].ToString(),
dtGetTop10UndeliveredSms.Rows[j]["Msg_Type"].ToString());
}
//Commit the transaction
//transaction.Complete();
//End this transaction scope by
disposing the transaction object
//transaction.Dispose();
log.Debug("Update complete");
}
//Sleep after each iteration
Thread.Sleep(1000);
}
}
}
catch(Exception ex)
{
log.Error(ex.Message);
}
log.Debug(string.Format("Exiting
UpdateDeliveryReportsDB Thread with Thread Id {0}",
Thread.CurrentThread.ManagedThreadId.ToString()));
}
}