C#支持通过多线程并行地执行代码,一个线程有它独立的执行路径,能够与其它的线程同时地运行。一个C#程序开始于一个单线程,这个单线程是被CLR和操作系统(也称为“主线程”)自动创建的,并具有多线程创建额外的线程。这里的一个简单的例子及其输出:
调用Start方法后,线程开始运行,线程一直到它所调用的方法返回后结束。
命名线程
线程可以通过它的Name属性进行命名,这非产有利于调试:可以用Console.WriteLine打印出线程的名字,Microsoft Visual Studio可以将线程的名字显示在调试工具栏的位置上。线程的名字可以在被任何时间设置——但只能设置一次,重命名会引发异常。
程序的主线程也可以被命名,下面例子里主线程通过CurrentThread命名:
没想到看不见的线程可以通过名字来区分,真是太可爱了。。
为了强化大家对线程的理解,再附上一个例子:
该示例创建一个名为 Worker 的类,该类包含辅助线程将执行的方法 DoWork。这实际上是辅助线程的 Main 函数。辅助线程将通过调用此方法来开始执行,并在此方法返回时自动终止。既然线程如此强大,为什么不多整几个呢?这里又引出了另一个问题。。。
何时使用多线程
多线程程序一般被用来在后台执行耗时的任务。主线程保持运行,并且工作线程做它的后台工作。对于Windows Forms程序来说,如果主线程试图执行冗长的操作,键盘和鼠标的操作会变的迟钝,程序也会失去响应。由于这个原因,应该在工作线程中运行一个耗时任务时 添加一个工作线程,即使在主线程上有一个有好的提示“处理中...”,以防止工作无法继续。这就避免了程序出现由操作系统提示的“没有相应”,来诱使用户 强制结束程序的进程而导致错误。模式对话框还允许实现“取消”功能,允许继续接收事件,而实际的任务已被工作线程完成。BackgroundWorker恰好可以辅助完成这一功能。
何时不要使用多线程
多线程也同样会带来缺点,最大的问题是它使程序变的过于复杂,拥有多线程本身并不复杂,复杂是的线程的交互作用,这带来了无论是否交互是否是有意的,都会 带来较长的开发周期,以及带来间歇性和非重复性的bugs。因此,要么多线程的交互设计简单一些,要么就根本不使用多线程。除非你有强烈的重写和调试欲 望。当用户频繁地分配和切换线程时,多线程会带来增加资源和CPU的开销。
多线程的应用十分广泛,但是有利有弊,大家编程的时候注意平衡就好。
下面讲下自己学习“流”的一些心得,在我看来,C#的数据传输都是靠流来实现,数据随着涓涓细流流向他们该去的地方,精确无误,十分高效。特别是文件的传输,万万离不开流。
MemoryStream类用于向内存而不是磁盘读写数据。MemoryStream封装以无符号字节数组形式存储的数据,该数组在创建 MemoryStream对象时被初始化,或者该数组可创建为空数组。可在内存中直接访问这些封装的数据。内存流可降低应用程序中对临时缓冲区和临时文件 的需要。
memoryStream的使用实例:
using System;
using System.IO;
using System.Text;
class program{
static void Main()
{
int count;
byte[] byteArray;
char[] charArray;
UnicodeEncoding uniEncoding=new UnicodeEncoding();
byte[] firstString=uniEncoding.GetBytes("努力学习");
byte[] secondString=uniEncoding.GetBytes("不做C#中的菜鸟");
using (MemoryStream memStream=new MemoryStream(100))
{
memStream.Write(firstString,0,firstString.Length);
count=0;
while(count<secondString.Length)
{
memStream.WriteByte(secondString[count++]);
}
Console.WriteLine("Capacity={0},Length={1},Position={2}\n",memStream.Capacity.ToString(),memStream.Length.ToString(),memStream.Position.ToString());
memStream.Seek(0, SeekOrigin.Begin);
byteArray=new byte[memStream.Length];
count=memStream.Read(byteArray,0,20);
while(count<memStream.Length)
{
byteArray[count++]=Convert.ToByte(memStream.ReadByte());
}
charArray=new char[uniEncoding.GetCharCount(byteArray,0,count)];
uniEncoding.GetDecoder().GetChars(byteArray,0,count,charArray,0);
Console.WriteLine(charArray);
Console.ReadKey();
}
}
}
关于文件流的写入与读出,这里提供给读者两个代码片段:
文件流写入:
private void btnChooseOpenFile_Click(object sender, EventArgs e)
{
//选择文本框 对象
OpenFileDialog ofd = new OpenFileDialog();
ofd.InitialDirectory = @"C:\Users\John\Desktop";
//如果用户确定
if (ofd.ShowDialog() == DialogResult.OK)
{
//将用户选择的文件路径显示在文本框上
txtFilePathOpen.Text = ofd.FileName;
}
}
//保存文件
private void btnSave_Click(object sender, EventArgs e)
{
string strContent = txtInputSave.Text.Trim();
//创建文件流(文件路径,文件操作、创建)
using (FileStream fs = new FileStream(txtFilePathOpen.Text, FileMode.Create))
{
//将字符串字符串转成byte数组
byte[] byteFile = Encoding.UTF8.GetBytes(strContent);
//参数:要写到文件的数据数组,从第几个开始写,一共写多少个
fs.Write(byteFile, 0, byteFile.Length);
MessageBox.Show("保存成功!");
}
}
文件流读出:
private void btnChooseOpenFile2_Click(object sender, EventArgs e)
{
//选择文本框 对象
OpenFileDialog ofd = new OpenFileDialog();
ofd.InitialDirectory = @"C:\Users\John\Desktop";
//如果用户确定
if (ofd.ShowDialog() == DialogResult.OK)
{
//将用户选择的文件路径显示在文本框上
txtFilePathOpen2.Text = ofd.FileName;
}
}
private void btnRead_Click(object sender, EventArgs e)
{
using (FileStream fs = new FileStream(txtFilePathOpen2.Text, FileMode.Open))
{
byte[] byteData = new byte[1024 * 1024 * 4];
//返回读取的长度
int length=fs.Read(byteData,0,byteData.Length);
if (length > 0)
{
string strData = Encoding.UTF8.GetString(byteData);
txtOutputRead.Text = strData;
MessageBox.Show("读取成功");
}
}
}
目前我对流的理解止步于此,不过今后我会对这方面多加关注,如果这篇博客有什么不对的地方,还望大家指出,共同学习,一起进步。