浅学C#(25)——任务Task

版权声明:转载请注明出处 https://blog.csdn.net/le_17_4_6/article/details/86656281
任务Task

任务在后台使用ThreadPool
在安排需要完成的工作时,任务提供了非常大的灵活性。
可以定义连续的工作,在一个任务完成后需要执行什么工作
可以在层次结构中安排任务,例如父任务可以创建新的子任务,这可以创建一种依赖关系
启动任务
可以使用TaskFactory类或Task类的构造函数和Start()方法

static void TaskMethod()
{
    Console.WriteLine("running in a task");
    Console.WriteLine("Task id: {0} {1}", Task.CurrentId, 
     Thread.CurrentThread.ManagedThreadId);
}
// using task factory
TaskFactory tf = new TaskFactory();
Task t1 = tf.StartNew(TaskMethod);

// using the task factory via a task
Task t2 = Task.Factory.StartNew(TaskMethod);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

// using Task constructor
Task t3 = new Task(TaskMethod);
// t3.Start();
t3.RunSynchronously();//任务也会启动,但是在调用者的当前线程中运行,调用者需要一直等待该任务结束

Task t4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness);
t4.Start();

TaskCreationOptions选项
设置LongRunning选项,可以通知任务调度器,该任务需要较长时间运行,这样调度器更可能使用新线程
如果该任务关联到父任务上,而父任务取消了,该任务也要取消,此时应设置AttachToParent选项
如果任务应以公平的方式与所有其他任务一起处理,应设置该选项为PerferFairness

连续的任务
通过任务,可以指定在任务完成后,应开始运行另一个特定任务 。例如,一个使用前一个任务的结果的新任务,如果前一个任务失败了,这个任务就应执行一些清理工作
连续处理程序有一个Task类型的参数

static void DoOnFirst()
{
   Console.WriteLine("doing some task {0}", Task.CurrentId);
        Thread.Sleep(3000);
}

static void DoOnSecond(Task t)
{
    Console.WriteLine("task {0} finished", t.Id);
    Console.WriteLine("this task id {0}", Task.CurrentId);
    Console.WriteLine("do some cleanup");
    Thread.Sleep(3000);
}

Task t1 = new Task(DoOnFirst);
Task t2 = t1.ContinueWith(DoOnSecond);
Task t3 = t1.ContinueWith(DoOnSecond);
Task t4 = t2.ContinueWith(DoOnSecond);   

连续任务通过在任务上调用ContinueWith( )来定义,也可以使用TaskFactory类来定义
调用DoOnSecond方法的新任务应在任务t1结束时立即启动
在一个任务结束时,可以启动多个任务
连续任务也可以有另一个连续任务
无论前一个任务是如何结束的,连续任务总是在前一个任务结束时启动
使用TaskContinuationOptions枚举中的值,可以指定任务只有在起始任务成功(或失败)结束时启动
Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);

任务层次结构

父任务和子任务
一个任务在另一个任务内部创建,构成父子关系
如果父任务在子任务之前结束,父任务的状态就显示为WaitingForChildrenToComplete。子任务也结束时,父任务的状态就显示为RanToCompletion
如果父任务使用TaskCreationOptions枚举中的DetachedFromParent创建子任务,以上就无效
取消父任务就会取消子任务

static void ParentAndChild()
{
    Task parent = new Task(ParentTask);
    parent.Start();
    Thread.Sleep(2000);
    Console.WriteLine(parent.Status);
    Thread.Sleep(4000);
    Console.WriteLine(parent.Status);

}

static void ParentTask()
{
    Console.WriteLine("task id {0}", Task.CurrentId);
    Task child = new Task(ChildTask); 
     child.Start();
    Thread.Sleep(1000);
    Console.WriteLine("parent started child");
   // Thread.Sleep(3000);
}
static void ChildTask()
{
    Console.WriteLine("child");
    Thread.Sleep(5000);
    Console.WriteLine("child finished");
}

任务的结果
使用Task类的泛型版本Task,就可以定义返回某个结果的任务的返回类型
为了返回某个结果任务调用的方法可以声明为带任意返回类型

例:有10个线程对一个银行账户取钱,每个线程 进行100次取钱操作。观察同步和不同步情况下的交易状况。

 class Account
    {
        int balance;
        Random r = new Random();
        public Account(int initial)
        {
            balance = initial;
        }
        int withdraw(int amount)
        {
            Console.WriteLine(Thread.CurrentThread.Name + ":");
                     if (balance < 0)
            {
                throw new Exception("余额为负!");
            }
            lock (this)
            {
                if (balance >= amount)
                {
                    Console.WriteLine("原有余额:" + balance);
                    Console.WriteLine("支取余额:-" + amount);
                    balance -= amount;
                    Console.WriteLine("现有余额:" + balance);
                    return amount;
                }
                else
                {
                    Console.WriteLine("支取不成功");
                    return 0;  //拒绝交易
                }
            }
		public void DoTransactions()
        {
            //支取随机的金额100次
            for (int i = 0; i < 100; i++)
            {
                try
                {
                    withdraw(r.Next(1, 100));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }
     	static void Main(string[] args)
        {
            //建立10个线程同时进行交易
            Thread[] threads = new Thread[10];
            Account acc = new Account(10000);
            for (int i = 0; i < 10; i++)
            {
                Thread t = new Thread(new ThreadStart(acc.DoTransactions));
                t.Name = "thread" + i;
                threads[i] = t;
            }
            for (int i = 0; i < 10; i++)
                threads[i].Start( );
        }

取消同步

    int  Withdraw(int amount) 
     {  
        ……
        if (balance >= amount)
            {
                Console.WriteLine("原有余额:" + balance);
                Console.WriteLine("支取余额:-" + amount);
                balance -= amount;
                Console.WriteLine("现有余额:" + balance);
                return amount;
            }
            else
            {
                Console.WriteLine("支取不成功");
                return 0;  //拒绝交易
            }
       }            

同步上下文
C#中,只要类从ContextBoundObject继承,并在类定义上增加Synchronization属性标记即可

[Synchronization( )]
public  class   A: ContextBoundObject
{
    //类的成员定义…    
}

创建A类对象会自动启动同步
在同一上下文中,任一时刻最多只能有一个线程访问该对象的成员
但这种方式很耗费系统资源,运行速度会有明显下降
只用于对象的每个成员都需要进行同步控制的场合

ReaderWriterLockSlim

为了使锁定机制允许锁定多个读取器(而不是一个写入器)访问某个资源,可以使用ReaderWriterLockSlim
这个类提供了一个锁定功能,如果没有写入器锁定资源,就允许多个读取器访问资源,但只能有一个写入器锁定该资源
ReaderWriterLockSlim类的属性可获得读取阻塞的锁定,如EnterReadLock()和TryEnterReadLock()方法
可以使用EnterWriteLock()和TryEnterWriteLock()方法获得写入锁定
如果任务先读取资源,之后写入资源,就可以使用EnterUpgradableReadLock()和TryEnterUpgradableReadLock()获得可升级的读取锁定。有了这个锁定,就可以获取写入锁定,而无需释放读取锁定
该类的几个属性提供了当前锁定的相关信息,如CurrentReadCount、WaitingReadCount、WaitingUpgradableReadCount和WaitingWriteCount

扫描二维码关注公众号,回复: 5675991 查看本文章

猜你喜欢

转载自blog.csdn.net/le_17_4_6/article/details/86656281