1.应用背景:大量new对象,且对象拥有相似元素,比如c/s架构里面client和server的一个关系,我们假如每次都会new一个新的对象用于处理client的请求,那么在server端就会产生大量的对象,从而导致内存溢出。
解决办法:享元模式,(一种池技术【对象池】)
实现过程:
1.我们有一个Server用来处理Client 的请求:它其实是一个工厂,并携带对象池
public class Server
{
/// <summary>
/// 对象池,说白了,就是一个存储对象的字典或者List=。=
/// </summary>
public static Dictionary<int, Client> poolDic = new Dictionary<int, Client>();
public Server()
{
Thread thread = new Thread(run);
thread.Start();
}
public void run()
{
Server.getInstance(1).requestInfo = "ssss";
Server.getInstance(1).writeInfo = "ssss";
}
public static Client getInstance(int key)
{
if (poolDic.ContainsKey(key) == true)
{
return poolDic[key];
}
else
{
Client client = new Client(key);
poolDic.Add(key, client);
return client;
}
}
}
2.定义请求对象Client
public class Client
{
public Client(int _id)
{
this.ID = _id;
}
public int ID;
public string requestInfo;
public string writeInfo;
public override string ToString()
{
string reStr = "";
reStr += "ID:" + ID+"\n";
reStr += "requestInfo:" + requestInfo + "\n";
reStr += "writeInfo:" + writeInfo + "\n";
return reStr;
}
}
3.简单的场景:
static void Main(string[] args)
{
Client client = Server.getInstance(1);
client.requestInfo = "登录请求";
client.writeInfo = "acc:123,psd:123";
Client client2 = Server.getInstance(2);
client2.requestInfo = "登录请求";
client2.writeInfo = "acc:sdd,psd:sdd";
Console.WriteLine(client.ToString());
Console.WriteLine(client2.ToString());
//这时,client2登录进去了,它进行了一个新的请求:更换签名
Client client2_ = Server.getInstance(2);
client2_.requestInfo = "进行了一个新的请求!";
Console.WriteLine(client2_.ToString());
}
4.运行结果和结果 说明:
我们看第三个ToString的信息,我们不难发现,其实这个对象是来自于对象池,而不是字典,从这个角度理解,我们可以确定,我们的享元模式是成功了的
5.线程安全
//在这个模式里面有个有趣的问题,如果我们分配给一个班级40个学生一人一个ID
//然后我们使用多线程去处理这40个ID对应的Client的请求,这样做,它其实没问题
//但假如,我们分成4个组,也就是对象池中只保存4个对象用来处理40个Client的通信,那就会遇到线程安全这种问题
//什么是线程安全呢?
//举个例子,就是说我线程A在处理AO对象时,是有一个时间的,在这个时间内我A还没处理完对象,B就开始对AO动手了
//那么AO对象的数值就会被修改,修改成我们无法预测的状态,这是一个非常可怕的事情,尤其是C/S架构里面
//举个例子,你在玩游戏,你在在商城里面购买了一些游戏币同时杀怪的时候,假如,有个傻逼程序员使用两个线程去处理
//这一同时的动作Action,并且,操作的都是Client这个对象,在这个对象里面,存在一个公共值,叫做Value
//购买命令发出时,Thread_purch对Value进行操作,然后Value = 1000,同时,你杀怪的攻击1000000倍Thread_atk执行了
//这时候Value = 1000000,然后Thread_purch返回给你1000000元宝(1:10)也就是说,你凭空赚了9900块,这家游戏公司倒闭是妥妥的
//不知道你是否看懂了线程安全,现在,我们就面对一下在锐减对象时,产生的线程安全
我们定义一个方法,在对象里面写入数据
我们定义两个Run方法并用Thread来运行它
我们来说明一下,为什么要这样做,我们的目的是检测多线程是不是会造成不安全,所以,我们给两个对象确定的相同的两个数据,也就是说Client里面的requestInfo和writeInfo应该是相同的,我们在场景中打印相关信息的时候,不应该出现不一样的情况,假如出现不一样的情况,那么久只有一种可能,那就是它的数据被其它线程给修改了,是吧?
我们来看一下检测的代码:
然后我们看一下结果:
显然,出现了线程不安全的情况。
6.怎么解决不安全的情况呢?
显然,答案是我们不要试图去共用一个对象,四十个客户就给它四十个客户好了,不要只给它四个Client 去处理四十个,这样很容易造成线程不安全的情况。
危险的时候还会造成线程的死锁,然后电脑宕机。