所以在我的程序中发生了一些奇怪的事情。
所以我有多个线程与ThreadPool.QueueUserWorkItem
排队。直到最近,线程池中的每个线程都会从一个数组中接收List<MyObject>
,并且每个线程都将列表插入到我的MySql数据库中。最近对列表处理的更改迫使我创建一个包含所有小列表的大列表(使用List.AddRange
)。然后我将此列表拆分为较小的部分,并将每个部分发送到之前的相同插入线程。现在,一切都与 7个或更少的线程完美配合。我的程序最初使用8个线程来执行所有操作,因为我记得读过线程应该是2 *个核心的东西。
现在,当我运行8个线程的程序时,它开始抛出各种似乎随机抛出的MySqlExceptions。
示例:所有线程查询从未更改的某个表。有些线程将返回select * from table fine,而1个随机线程会突然说该表不存在或者我的DataTable
为空,这是不可能的,因为它是相同的表和查询。我有各种其他异常,比如OpenDataReader,Connection必须有效和打开,NullReferenceException试图关闭Connection等。
注意:每个线程都拥有与数据库的单独连接,因此不应相互冲突。
由于代码很长,我不确定是否粘贴代码。另外,我不太确定我的代码是什么,因为我说当我使用8个或更多线程时出现问题。 我不想采取简单的方法,只使用7个线程,因为可能有一些东西,我不知道这将导致将来当我实施额外的更改时出现问题。 如果有人要我展示具体的作品,我可以相应地编辑我的问题。
public MySqlDb
{
MySqlConnection mySqlConnection;
MySqlDataAdapter mySqlDataAdapter;
public int ExecuteInsert(string query)
{
using (MySqlCommand mySqlCommand = new MySqlCommand())
{
mySqlCommand.CommandText = query;
using (mySqlCommand.Connection = GetConnection())
{
mySqlCommand.Connection.Open();
using (mySqlDataAdapter = new MySqlDataAdapter())
{
try
{
mySqlDataAdapter.InsertCommand = mySqlCommand;
return mySqlDataAdapter.InsertCommand.ExecuteNonQuery();
}
catch (MySqlException mse)
{
logger.Warn("MySqlException : " + mse.Message);
throw;
}
finally
{
mySqlCommand.Connection.Close();
}
}
}
}
}
public MySqlConnection GetConnection()
{
connectionString.DefaultCommandTimeout = 3600;
myConnection = new MySqlConnection(connectionString.GetConnectionString(true));
return myConnection;
}
}
我的连接字符串是从我的.xml文件加载的,只是一个基本的连接字符串
<add name="MySqlConnectionString" providerName="System.Data.SqlClient"
connectionString="Server=server_name;Database=db_name; Uid=my_username;Pwd=my_pass;charset=utf8;port=3306;" </add>
更新1:因此,在尝试找出问题之后,我一次减少了我执行的插入量。每个线程都包含一个表示MySql表的Object。从打印出查询的例外情况之一来看,似乎Insert()
被调用两次而没有执行实际的mySql.ExecuteInsert(query.ToString())
。除非某些变量在线程之间共享,即使每个变量都有自己独立的对象,我也没有看到它应该这样做的任何理由。以下是相关代码。如果这个问题太长而无法作为正确的问题阅读,请对此进行评论以及如何进行评论。
class PartsInventories
{
MySqlDb mySql;
public const String PARTS_INVENTORIES = "parts_inventories";
// find the optimum of best perfomance while not raising MAX_PACKETS_ALLOWED exception - right now 6000 - 2012-06-05
public const int MAX_PARTS_IN_QUERY = 1;
public StringBuilder query;
bool havePartsToInsert = false;
int insertedCount = 0;
public PartsInventories()
{
mySql = new MySqlDb();
}
public void Init(int usersCompanyId, string mailId,
string appendDate, bool toInsertPrices, string fileId)
{
// init query string and shared query variables here
insertedCount = 0;
query = new StringBuilder(GetQueryString());
}
private string GetQueryString()
{
return @"INSERT INTO parts_inventories (part_number,base_part_number,
date_code,quantity,price,currency_types_id,delivery,rohs,parts_pkg_type_id,parts_mfg_id,
users_company_id,notes,in_stock,mail_id,append_date,file_id,ad_price) VALUES ";
}
private void InsertPart(ParsedInformation part, bool toInsertPrices)
{
// assign all specific query variables here
query.AppendFormat("({0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},'{14}',{15},{16}),",
PartNumber, BasePartNumber, DateCode, Quantity, Price, CurrencyTypesId, Delivery,
Rohs, PartsPkgTypeId, PartsMfgId, UsersCompanyId, Notes, InStock, MailId, AppendDate, FileId, adPrice);
}
public void InsertPart(ParsedInformation part)
{
InsertPart(part, toInsertPrices);
insertedCount++;
if (insertedCount % MAX_PARTS_IN_QUERY == 0)
{
Insert();
query = new StringBuilder(GetQueryString());
}
}
public void Insert()
{
if (!havePartsToInsert) return;
query.Remove(query.Length - 1, 1);
query.Append(@" ON DUPLICATE KEY UPDATE part_number = VALUES(part_number), date_code = VALUES(date_code),
quantity = VALUES(quantity), price = VALUES(price), currency_types_id = VALUES(currency_types_id),
delivery = VALUES(delivery),rohs = VALUES(rohs), parts_pkg_type_id = VALUES(parts_pkg_type_id),
parts_mfg_id = VALUES(parts_mfg_id), notes = VALUES(notes), in_stock = VALUES(in_stock),
mail_id = VALUES(mail_id), append_date = VALUES(append_date), sent_rfq = NULL, from_website = NULL, from_lti = NULL,
file_id = VALUES(file_id), ad_price = VALUES(ad_price);");
try
{
logger.DebugFormat("Inserted/Updated {0} parts.", mySql.ExecuteInsert(query.ToString()));
havePartsToInsert = false;
}
catch
{
logger.Debug("problem inserting");
havePartsToInsert = false;
throw;
}
}
}
每个线程都是一个类,并从创建和调用线程的类接收列表。
class ThreadInserter
{
ManualResetEvent doneEvent;
PartsInventories partsInventories;
public ThreadSafeInserter(List<ParsedInformation> allParts, ManualResetEvent doneEvent, string mailId, int usersCompanyId,
string appendDate, bool toInsertPrices, bool hasStockInBody, string fileId)
{
this.doneEvent = doneEvent;
partsInventories = new PartsInventories();
}
private void InsertAllParts(int threadIndex)
{
partsInventories.Init(usersCompanyId, mailId, appendDate, toInsertPrices, fileId);
if (allParts != null)
{
foreach (ParsedInformation part in allParts)
{
// another thread has thrown an exception so aborting all threads
// set from the main invoking the threadcallback
if (doneEvent.WaitOne(0))
{
logger.Warn("Thread aborted.");
return;
}
partsInventories.InsertPart(part);
}
}
partsInventories.Insert();
doneEvent.Set();
}
}