线程与同步

系统中的线程与进程
进程包含window句柄,文件系统和其他核心对象等,每个进程系统都会分配一个虚拟内存,每个进程最少有一个线程。
线程是运行程序必须的独立的指令流,线程也有自己的堆栈(内存)
应用程序的内存和堆由一个进程中的所有线程共享,所引线程之间的交换非常快,但是由于他们共享所在的同一进程的
的资源,所以操作起来会很困难,后面关于多线程会介绍。

通俗的将就是 应用程序是由进程运行的,会为进程分配资源(内存空间等),进程中又有一些线程,会为每个线程分配资源
线程可以操作进程中的所有资源,应用程序有时要同时进行几个操作,而线程又是彼此独立的指令流,所以同一时间有多个线程在运行,(此处所说的多个线程同时运行都不考虑线程的运行原理只是通俗的说法)而同一资源有可能有多个线程使用。这样就会带来线程同步的问题,我们可以这些未处理的资源为线程不安全的资源。在后面会介绍多线程操作。
下面将详细介绍线程的操作
1.前台线程和后台线程
  线程可以分为前提线程和后台线程。只要有一个前台线程在运行,应用程序就会运行,主线程Main()方法运行结束,
还有几个前台线程在运行,程序就会在运行,当所以的前台的线程都结束了后程序才会停止。(不考虑后台线程)
Thread类创建的线程默认都是前台线程,线程池的线程总是后台线程。
Thread类创建的线程有 IsBackGround属性如果为true是为可以设置线程为后台线程
实例:
class Program
{
static void main()
{
Thread t1=new Thread(ThreadMain);
t1.Name="boy";
t1.IsBackGround=false;
t1.Start();
Console.WriteLine("main方法结束");
}

static void ThreadMain()
{
Console.WriteLine("我们都是好孩子{0}",Thread.CurrentThread.Name);
Thread.Sheep(3000);
Console.WriteLine("漂亮的好孩子{0}",Thread.CurrentThread.Name);
}
}
此时会控制台上会打印
“我们都是好孩子boy”
“main方法结束”
“漂亮的好孩子boy”

如果加上t1.IsBackGround=true;
控制台会打印
“我们都是好孩子boy”
”main方法结束“

线程的操作
用Thread类创建线程
       class Program
    {
        static void Main(string[] args)
        {
            //StateObject so = new StateObject();
            //for (int i = 0; i < 20; i++)
            //{
            //    new Thread(new SampleThread().RaceCondition).Start(so);
            //}
            StateObject so = new StateObject();
            ThreadMethod tm = new ThreadMethod();
            //Thread t1 = new Thread(tm.MethodHasParam);
            //t1.Start("我们都是好孩子!");
            Thread t1 = new Thread(delegate() { tm.MethodHasParam("我们都是好孩子"); });
            t1.Start();
            Console.ReadKey();
        }
    }
    public class ThreadMethod
    {
        public void MethodNoParam()
        {
            Console.WriteLine("我是无参的方法");
        }
        public void MethodHasParam(object o)
        {
            Console.WriteLine("我是有参的方法");
            Console.WriteLine("参数是:{0}",o);
        }
    }

2. 线程池 ThreadPool
线程池原理:线程池中有一个个有线程组成的工作者列队,而要执行的操作也会形成一个工作项列队,工作项列队满足先进先出的原则(先进入工作项列队的先进入线程列队中处理)。工作者线程列队会处理进入的一个个要执行的操作,进入线程列队满足后进先出得原则(后进入的先处理),如果工作列队已满,还有需要执行的操作,要执行的操作(工作者)就回进入下一个线程列队,保证每一个开启的线程列队的所有线程都在工作,极大的提高了线程的利用率。
线程池也有默认容许的最大工作线程和I/O线程容量。一般双核处理器默认容许最大工作线程为50,I/O线程为1000个。不过此值可以设置,一般不会手动设置。当所有的工作线程都在工作时,工作者列队会等待直到有空闲线程为止。
线程池实例:
    class Program
    {
        static void Main(string[] args)
        {
            int nWorkerThreads;
            int nCompletionPortThreads;
            ThreadPool.GetMaxThreads(out nWorkerThreads,out nCompletionPortThreads);
            Console.WriteLine("工作线程:{0}",nWorkerThreads);
            Console.WriteLine("I/O线程:{0}",nCompletionPortThreads);
            for (int i = 0; i < 6; i++)
            {
                ThreadPool.QueueUserWorkItem(JopThread);
                  
            }
                Console.ReadKey();
        }

        static void JopThread(object state)
        {
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("i的值是:{0},当前线程的ID:{1}", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(50);
            }
        }
    }
注意:线程池中的线程都是后台线程,而且不能将其设置为前台线程,不能为线程池中线程设置优先级和名称。线程池中线程都由一个唯一标示的Id
(ManagedThreadId)

线程的同步:
由于线程独立的指令流,并举线程共享同一进程中的资源。当多个线程操作同一数据,也就是线程的同步。产生线程同步如下:
为了避免线程的同步  可以使用lock关键字
lock关键字
public class Dome
{
public void DoThis()
{
lock(this)
{

}
}
}
也可以:
   class Program
    {
        static void Main(string[] args)
        {
            int threadNum=20;
            Thread[] t=new Thread[threadNum];
            StateNum sn = new StateNum();
          //  sn.Num = 1;
            for(int i=0;i<threadNum;i++)
            {
                t[i] = new Thread(delegate() { DaleNum.AddNum(sn); });
                t[i].Start();
              
            }
            for (int i = 0; i < threadNum; i++)
            {
                t[i].Join();
            }
           
                Console.WriteLine("结果是:{0}",sn.Num);
            Console.ReadKey();
        }
    }
    public class StateNum
    {
        public int Num
        {
            get;
            set;
        }
    }
    public class DaleNum
    {
        private static object obj = new object(); 
        public static void AddNum(StateNum sn)
        {
            lock(obj)
            {
                for (int i = 0; i < 5000; i++)
                {
                    sn.Num += 1;
                }
            }
        }
    }


线程的死锁:
使用lock关键字时可能出现死锁:
例如:方法Deadlock1()和Deadlock2()现在改变两个对象s1和s2的对象。这就进行了两个锁定。方法Deadlock1()先锁定s1在锁定s2,方法Deadlock2()
先锁定s2再锁定s1。有可能出现这种情况,Deadlock1()方法等待Deadlock2();解除s2锁定,而Deadlock2等待Deadlock1解除s1的锁定,此时就
会出现死锁
解除死锁的方法:
lock语句C#编译器会解析为Monitor类
lock(obj)
{
//dfadfasdfasdfa
}
等同于:
Monitor.Enter(obj);
try
{
//aaaa
}finally
{
Monitor.Exit(obj);
}
下面就介绍避免死锁的方法
Monitor类可以获得锁定的超时值,这样就不会无限期的等待获得锁定。
可以使用TryEnter方法,给它传送一个超时值,等到获得锁定最大超时值
如果得到obj会返回true,如果锁定超过指定时间,则返回false,执行其他操作
if(Monitor.TryEnter(obj,500))
{
try
{

}finally
{
Monitor.Exit();
}
}

这样就解决了死锁问题

还有关于线程类Thread的一些方法Join,就不坐介绍了

关于WindowsForm的跨线程调用和一些操作线程的类,这里就不介绍了,






posted @ 2012-10-01 00:03  勤奋的小码  阅读(988)  评论(0编辑  收藏  举报