我有一个线程数组,它们从私有消息队列中检索消息,将它们反序列化为日志条目对象,并将日志条目对象的属性存储在SQL Server数据库表中。
这是我创建和启动线程的代码。
try
{
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(new ThreadStart(this.logEntriesToDatabase));
threads[i].Start();
}
}
catch (ThreadStateException ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
catch (OutOfMemoryException ex)
{
MessageBox.Show("Not Enough Memory Please Close Other Applications To Continue", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
每个线程执行一个函数logentriestodatabase()
while(true)
{
#region Retrieves Message from Message Queue and Deserialized it to a Log Entry Object.
#region Sleep Time for Current Thread
Thread.Sleep(180);
#endregion
#region Check to See Whether Queue Is Empty. If so go back to start of while loop
if (q1.GetAllMessages().Length == 0)
{
continue;
}
#endregion
#region Message retrieval and Deserialization Code
System.Messaging.Message m = this.q1.Receive();
m.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
LogEntry lg = BinaryLogFormatter.Deserialize(m.Body.ToString());
#endregion
#endregion
#region Insert Log Entry Into Database
#region Define a new SQL Connection with username and password specified in App.Config, an SQL Transaction and database queuries
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["LogReader"].ConnectionString);
SqlTransaction transaction;
string query_insert_into_logentry = "INSERT INTO logentry" + "(message, priority, processname, severity, accountid, ipaddress, servername, servertype, timestamp)" + "VALUES ('" + lg.Message + "'," + lg.Priority + ",'" + lg.AppDomainName + "','" + lg.Severity.ToString() + "','" + lg.ExtendedProperties["AccountID"].ToString() + "','" + lg.ExtendedProperties["IpAddress"].ToString() + "','" + lg.ExtendedProperties["ServerName"].ToString() + "','" + lg.ExtendedProperties["ServerType"].ToString() + "','" + lg.TimeStamp.ToString() + "')";
string query_insert_into_category = "INSERT INTO category (category) VALUES ('" + lg.Categories.First().ToString() + "')";
#endregion
#region Begin and Terminates Transaction and Closes the SQL Connection Catches any SQL Exception Thrown and Displays Them
try
{
conn.Open();
transaction = conn.BeginTransaction();
new SqlCommand(query_insert_into_logentry, conn, transaction).ExecuteNonQuery();
new SqlCommand(query_insert_into_category, conn, transaction).ExecuteNonQuery();
transaction.Commit();
conn.Close();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
return;
}
#endregion
#endregion
}
现在每当我运行此程序时,消息队列变空,程序就会挂起。 我似乎无法弄清楚为什么。我试图给q1.Receive()函数一个TimeSpan,但是没有用。我用180毫秒的时间调用了睡眠方法,但它仍然不起作用。也许是因为q1.Receive方法在遇到空队列时将当前线程发送到阻塞状态。
请帮助我接受我的想法。
答案 0 :(得分:1)
您可以使用MessageQueue.BeginReceive / EndReceive异步读取消息,而不是在紧密循环中同步读取消息并阻塞多个线程。提出了类似的问题here。
如果您使用的是.NET 4.0或更高版本,则可以从BeginReceive / EndReceive对创建一个Task,并使用ContinueWith处理该消息,而无需创建新线程。在.NET 4.5中,您可以使用asyc/await
关键字使处理更简单,例如:
private async Task<Message> MyReceiveAsync()
{
MessageQueue queue=new MessageQueue();
...
var message=await Task.Factory.FromAsync<Message>(
queue.BeginReceive(),
queue.EndReceive);
return message;
}
public async Task LogToDB()
{
while(true)
{
var message=await MyReceiveAsync();
SaveMessage(message);
}
}
即使LogToDB
使用`while(true),循环也会异步执行。
要结束循环,您可以将CancellationToken传递给LogToDB
和end processing cooperatively:
public async Task LogToDB(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
var message=await MyReceiveAsync();
SaveMessage(message);
}
}
这样可以避免创建多个线程和计时器。
答案 1 :(得分:0)
当break
为空时,您可能需要continue
循环而不是queue
。 continue
导致无限循环,导致无响应行为。
更改
if (q1.GetAllMessages().Length == 0)
{
continue;
}
到的
if (q1.GetAllMessages().Length == 0)
{
break;
}
根据评论修改
您可以通过放置Thread.Sleep以允许其他线程获取CPU共享来为当前线程提供中断。我不会在这里使用Sleep,而是使用计时器System.Timers.Timer,并在固定的时间间隔后对MSMQ执行操作。
您可以使用MSMQEvent.Arrived事件代替计时器。
使用计时器。
void SumFun()
{
aTimer = new System.Timers.Timer(10000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 2000;
aTimer.Enabled = true;
}
活动宣言。
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
//Call the method for process MSMQ and do not use While loop to check queue.
}