我的客户端有两个界面,下面就简称为Form1 和Form2。
Form1有一个datagridview控件,需要有的操作就是在控件里循环载入数据库中的数据并清空再下一轮载入数据。
Form2的功能是监听网络请求并如果收到请求告诉Form1开始工作。
然后问题就来了如果 在Form1 中调用这个操作没有任何问题,如果用Form2调用这个操作的方法(这里是用了一个委托事件触发的,而且因为在调用Form中方法的同时还需要保持监听,所以用线程池做成异步来处理)就会返回main主函数抛出一个空指针异常,但是我断点调试没有找到空指针的对象。异常如图:
然后上错误代码:
Form1对控件操作:
public void test()
{
Monitor.Enter(this);
try
{
for (int i = 0; i < 7; i++)
{
Control.CheckForIllegalCrossThreadCalls = false;
string fn = list[i];
OpenDB(fn);
//Thread.Sleep(1000);
}
}
finally
{
Monitor.Exit(this);
}
}
void connectToDB(string fn)
{
m_dbConnection = new SQLiteConnection("Data Source=" + fn + ";Version=3;");
//m_dbConnection.SetPassword("abc");
m_dbConnection.SetPassword("1234LiaoQiu4321");
m_dbConnection.Open();
}
public void OpenDB(string fn)
{
try
{
string sql;
SQLiteCommand command;
connectToDB(fn);
sql = @"SELECT * FROM [评分细则] ORDER BY [步骤];";
command = new SQLiteCommand(sql, m_dbConnection);
SQLiteDataReader reader = command.ExecuteReader();
dataGridView2.Rows.Clear();
object[] obj = new object[10];
while (reader.Read())
{
for (int i = 0; i < 10; ++i)
{
obj[i] = reader[i];
}
dataGridView2.Rows.Add(obj);
}
reader.Close();
}
catch //(Exception ex)
{
MessageBox.Show("打开失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
finally
{
m_dbConnection.Close();
}
}
/// <summary>
/// 事件订阅
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void 其他窗口运行ToolStripMenuItem_Click(object sender, EventArgs e)
{
Form2 ab = new Form2();
ab.getEvent += test;
ab.Show();
}
Form2:
public delegate void getHandler();
public event getHandler getEvent;
private void button1_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(test), new object());
}
public void test(object val)
{
getEvent();
}
Form2这里是我写的一个模拟错误调用的demo,因为监听网络调用调式比较麻烦,所以用button模拟触发 收到消息这个动作。考虑过跨线程调用控件的问题,所以已经加了禁用语句。
问题奇怪的地方有三个:1.如果是Form1本身的线程调用,或者Form2中不加异步就不会抛异常;2,调试时发现循环第一次不会出错,也就是会往控件里加载一次数据,但是第N次循环就可能会出现问题,出错的循环次数是哪一次无法确定,但是肯定会有一次出错。3.报错的位置不在调试时卡住的地方而是回到main主函数报错
而且目前找到一种解决办法就是在循环提内加入延迟,就是Thread.Sleep(500),这样也不会报错,但是不理解这样做就不报错的原因