新手使用多线程引发的困扰

最近在写个短信服务用于后台发送短信,短信分类有好几只,考虑到发送效率遂采用多线程发送。

短信发送的接口是由供应商提供的,包含了一个示例项目。

通过修改供应商提供的示例,遇到了居然花了较长时间才解决的问题,这问题是我第一次碰到所以记录一下以备后用。

ContractedBlock.gif ExpandedBlockStart.gif 接口方法声明
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace SMS
ExpandedBlockStart.gifContractedBlock.gif
{

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
    
/// 概述:引入对动态库dll的几个接口的调用
    
/// 备注:依赖于 SMS.dll
    
/// </summary>


    
public class smsDll
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
const string dllFile = "Lib\\xal.dll";  //请根据文件位置配置此值

        [DllImport(dllFile, CharSet 
= CharSet.Ansi)]
        
public static extern int Login(string Host, string Port, string Username, string Password);

        [DllImport(dllFile, CharSet 
= CharSet.Ansi)]
        
public static extern int Send(int ClientID, string Mobiles, string Msg, string ExNum, string AtTime, string ExParam,
                                       StringBuilder ResultValue, 
ref int ResultSize);

        [DllImport(dllFile, CharSet 
= CharSet.Ansi)]
        
public static extern int GetProperty(int ClientID, StringBuilder ResultValue, ref int ResultSize);
    }

}

using System;
using System.Collections.Generic;
using System.Text;

namespace SMS
ExpandedBlockStart.gifContractedBlock.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
    
/// 概述:对短信的相关功能简单作了实现封装  
    
/// 备注:依赖于 smsDll.cs
    
/// </summary>

    public class smsWorker:IDisposable 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
private int ClientID = 0;   
        
//属性
        public String Host = "123.123.123.123";
        
public String Port = "8000";
        
public String Username = "";
        
public String Password = "";

        
//方法

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  发送 全参数 */
        
public int Send(string Mobiles,string Msg,string ExNum,string AtTime,string ExParam,out string ResultStr)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            ResultStr 
= null;
            
if (!_CheckNet()) return -1;
            
int rtLen = 0
            StringBuilder rtBuf
=new StringBuilder(1024);
            
int rt = 0;
            rt 
= smsDll.Send(ClientID, Mobiles, Msg, ExNum, AtTime, ExParam, rtBuf, ref rtLen);
            ResultStr
=rtBuf.ToString();
            rtBuf.Length
=0;  
            
return rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  发送 简易 */
        
public string Send(string Mobiles, string Msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
string Rt = "";
            Send(Mobiles, Msg, 
"123456""保留字一=1232""MsgType=71"out Rt);
            
return Rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  获得属性(包括余额信息)  */
        
public int GetProperty(out string ResultStr)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            ResultStr 
= null;
            
if (!_CheckNet()) return -1;
            
int rtLen = 0;
            StringBuilder rtBuf 
= new StringBuilder(1024);
            
int rt = smsDll.GetProperty(ClientID, rtBuf, ref rtLen);    
            ResultStr 
= rtBuf.ToString();
            rtBuf.Length 
= 0;
            
return rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  登出  */
        
public void Logout()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
if (ClientID > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                smsDll.Logout(ClientID);
                ClientID 
= 0;
            }

            
return;
        }

   
        
private bool _CheckNet()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            StringBuilder str
=new StringBuilder(1024);
            
int RS = 0;
            
int r = smsDll.GetProperty(ClientID, str, ref RS);
            
if (r!=0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                ClientID 
= smsDll.Login(Host, Port, Username, Password);
            }

            
return ClientID > 0
        }


ContractedSubBlock.gifExpandedSubBlockStart.gif        
IDisposable 成员#region IDisposable 成员

        
public void Dispose()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Logout();            
        }

        
#endregion

    }

}

 

ContractedBlock.gif ExpandedBlockStart.gif 接口方法封装类
using System;
using System.Collections.Generic;
using System.Text;

namespace SMS
ExpandedBlockStart.gifContractedBlock.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
    
/// 概述:对短信的相关功能简单作了实现封装  
    
/// 备注:依赖于 smsDll.cs
    
/// </summary>
    public class smsWorker:IDisposable 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
private int ClientID = 0;   
        
//属性
        public String Host = "1.1.1.1";
        
public String Port = "8000";
        
public String Username = "";
        
public String Password = "";

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  发送 全参数 */
        
public int Send(string Mobiles,string Msg,string ExNum,string AtTime,string ExParam,out string ResultStr)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            ResultStr 
= null;
            
if (!_CheckNet()) return -1;
            
int rtLen = 0
            StringBuilder rtBuf
=new StringBuilder(1024);
            
int rt = 0;
            rt 
= smsDll.Send(ClientID, Mobiles, Msg, ExNum, AtTime, ExParam, rtBuf, ref rtLen);
            ResultStr
=rtBuf.ToString();
            rtBuf.Length
=0;  
            
return rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  发送 简易 */
        
public string Send(string Mobiles, string Msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
string Rt = "";
            Send(Mobiles, Msg, 
"123456""保留字一=1232""MsgType=71"out Rt);
            
return Rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  登出  */
        
public void Logout()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
if (ClientID > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                smsDll.Logout(ClientID);
                ClientID 
= 0;
            }

            
return;
        }
   
        
private bool _CheckNet()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            StringBuilder str
=new StringBuilder(1024);
            
int RS = 0;
            
int r = smsDll.GetProperty(ClientID, str, ref RS);
            
if (r!=0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                ClientID 
= smsDll.Login(Host, Port, Username, Password);
            }

            
return ClientID > 0
        }


ContractedSubBlock.gifExpandedSubBlockStart.gif        
IDisposable 成员#region IDisposable 成员

        
public void Dispose()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Logout();            
        }

        
#endregion

    }

}


ContractedBlock.gif ExpandedBlockStart.gif 修改前的发送窗体代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace DLL
ExpandedBlockStart.gifContractedBlock.gif
{
    
public partial class FMain : Form
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        SMS.smsWorker sms 
= new SMS.smsWorker();

        
public FMain()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            InitializeComponent();
        }


        
//发送按钮调用事件
        private void ToSend(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            SMS.smsWorker sms 
= new SMS.smsWorker();
            
for (int i = 0; i < 100; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
string Rt = sms.Send("13012345678""测试内容");
            }

        }

    }

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

namespace DLL
ExpandedBlockStart.gifContractedBlock.gif
{
    
public partial class FMain : Form
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
public SMS.smsWorker sms = null;  

        
public FMain()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            InitializeComponent();
        }


    
//点击发送按钮调用的事件
        private void ToSend(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
string Rt= sms.Send(textBox1.Text, textBox2.Text);            
            
if (Rt!=null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                MessageBox.Show(
"成功");               
            }

            
else
                MessageBox.Show(
"失败");   
        }

    }

}


ContractedBlock.gif ExpandedBlockStart.gif 加入多线程的发送窗体代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace DLL
ExpandedBlockStart.gifContractedBlock.gif
{
    
public partial class FMain : Form
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        SMS.smsWorker sms 
= new SMS.smsWorker();

        
public FMain()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            InitializeComponent();
        }


        
private void ToSend(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Thread sendA 
= new Thread(new ThreadStart(SendFunctionA));
            sendA.Start();
            Thread sendB 
= new Thread(new ThreadStart(SendFunctionB));
            sendB.Start();
        }


        
//发送线程A
        private void SendFunctionA()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
for (int i = 0; i < 100; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
string Rt = sms.Send("13012345678""测试内容");
            }

        }


        
//发送线程B
        private void SendFunctionB()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
for (int i = 0; i < 100; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
string Rt = sms.Send("13012345678""测试内容");
            }

        }

    }

}


启动多线程运行没几秒钟后就抛出了System.AccessViolationException,提示信息为:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”
异常出现在接口封装类 _CheckNet函数中的该语句:

int r = smsDll.GetProperty(ClientID, str, ref RS);

原因查N久调试N遍,最后才确认为问题出在多线程访问资源的冲突

然后修改了接口封装类,新增了锁对象,并修改了_CheckNet函数与int Send

ContractedBlock.gif ExpandedBlockStart.gif 修改后的接口方法封装类
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace SMS
ExpandedBlockStart.gifContractedBlock.gif
{
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
    
/// 概述:对短信的相关功能简单作了实现封装  
    
/// 备注:依赖于 smsDll.cs
    
/// </summary>

    public class smsWorker:IDisposable 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
        
/// 定义锁对象
        
/// </summary>

        object locker = new object();

        
private int ClientID = 0;   
        
//属性
        public String Host = "1.1.1.1";
        
public String Port = "8000";
        
public String Username = "";
        
public String Password = "";

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  发送 全参数 */
        
public int Send(string Mobiles,string Msg,string ExNum,string AtTime,string ExParam,out string ResultStr)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            ResultStr 
= null;
            
if (!_CheckNet()) return -1;
            
lock (locker)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
int rtLen = 0;
                StringBuilder rtBuf 
= new StringBuilder(1024);
                
int rt = 0;
                rt 
= smsDll.Send(ClientID, Mobiles, Msg, ExNum, AtTime, ExParam, rtBuf, ref rtLen);
                ResultStr 
= rtBuf.ToString();
                rtBuf.Length 
= 0;
            }

            
return rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  发送 简易 */
        
public string Send(string Mobiles, string Msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
string Rt = "";
            Send(Mobiles, Msg, 
"123456""保留字一=1232""MsgType=71"out Rt);
            
return Rt;
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//*  登出  */
        
public void Logout()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
if (ClientID > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                smsDll.Logout(ClientID);
                ClientID 
= 0;
            }

            
return;
        }
   
        
private bool _CheckNet()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
lock (locker)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                StringBuilder str 
= new StringBuilder(1024);
                
int RS = 0;
                
int r = smsDll.GetProperty(ClientID, str, ref RS);
                
if (r != 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    ClientID 
= smsDll.Login(Host, Port, Username, Password);
                }

            }

            
return ClientID > 0
        }


ContractedSubBlock.gifExpandedSubBlockStart.gif        
IDisposable 成员#region IDisposable 成员

        
public void Dispose()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Logout();            
        }

        
#endregion

    }

}


之后运行程序就没有抛出异常,既然是资源冲突问题,那就不定义公共对象smsWorker,而分别在两个线程里定义smsWorker。运行起来仍然抛出与之前一样的异常,无奈还是改成定义公共对象smsWorker

问题虽然解决了,但问题的核心仍然没有搞明白,线程声明内的smsWorker对象,应该独立的不会出现冲突才是。

难道被调用类的对象只能存在一个?如果大型系统中不慎定义了两个对象,排错岂不是要疯掉了。

看来还应该好好学习一下线程相关的知识。

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

namespace DLL
ExpandedBlockStart.gifContractedBlock.gif
{
    
public partial class FMain : Form
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
public SMS.smsWorker sms = null;  

        
public FMain()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            InitializeComponent();
        }


        
private void ToSend(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
string Rt= sms.Send(textBox1.Text, textBox2.Text);            
            
if (Rt!=null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                MessageBox.Show(
"成功");               
            }

            
else
                MessageBox.Show(
"失败");   
        }

    }

}

转载于:https://www.cnblogs.com/allenjsl/archive/2009/07/11/1521334.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值