C# 检查线程/后台任务是否已完成或需要中止

C# 检查线程/后台任务是否已完成或需要中止,c#,.net,multithreading,task,C#,.net,Multithreading,Task,我对console/windows服务(我可以在任一模式下运行)应用程序有一个相当简单的要求: 从数据库中获取要处理的项的列表 在后台启动一个方法(只有一个,不需要更多)来处理一个项目 检查是否已完成或需要终止(通过sql查找) 在完成/中止下一个步骤时重复2 当没有更多的时候,睡一会儿,重复1 我对c#/.net相当陌生,我可以看到各种线程系统。在这种情况下,线程和任务哪个更好 在线程的情况下,我假设每个要处理的项目都类似于(此粗略代码): Thread thread = new MyThre

我对console/windows服务(我可以在任一模式下运行)应用程序有一个相当简单的要求:

  • 从数据库中获取要处理的项的列表
  • 在后台启动一个方法(只有一个,不需要更多)来处理一个项目
  • 检查是否已完成或需要终止(通过sql查找)
  • 在完成/中止下一个步骤时重复2
  • 当没有更多的时候,睡一会儿,重复1
  • 我对c#/.net相当陌生,我可以看到各种线程系统。在这种情况下,线程和任务哪个更好

    在线程的情况下,我假设每个要处理的项目都类似于(此粗略代码):

    Thread thread = new MyThread(new ThreadStart(this.SomeFunction));
    thread.Start();
    while(!finished) {
      if (!thread.IsAlive())
        finished=true; 
      else {
         //check database for early termination of job
         terminate=SomeChdck();
         if(terminate) { thread.Abort(); finished=true;}
    
      }
    }
    
    //返回并重复

    或者在任务的情况下,它类似于(同样,粗略地完成,并从网络上缩减):

    //返回并重复

    这些方法有什么不同吗(假设它们都能工作),我在某个地方读到Thread.Abort()被弃用了,但文档中没有提到


    谢谢。

    使用任务工厂。您可以使用以下方法等待任务完成:

    Task.WaitAll(task);
    
    而不是示例代码中的while循环

    此外,CancelationToken用于向任务发出信号,表示它应该取消,因此您需要自己执行检查

    但是,如果你考虑的话,你会更好,这也支持取消令牌。通过这种方式,您可以从DB中检索记录,然后将它们作为IEnumerable传递到管道,同时您可以自由地从单独的线程或内部线程取消它们。您可以开始处理第一条记录,而其余记录则在后台检索。管道将为要处理的每个元素的每个步骤创建一个后台任务。每个步骤的默认并行度为1。它非常快速和高效

    更新

    带有简洁、并行扩展、附加和反应式扩展的小示例

    var pipeline = Pipeline.Create<SomeType, bool>(st =>
    {
        //Do something with st
        return someBool; //some bool if you succeeded or not
    });
    var cts = new CancellationTokenSource();
    //cancel after 10s (just for fun)
    Observable.Timer(TimeSpan.FromSeconds(10)).Subscribe(s => cts.Cancel());
    using (var conn = new SqlConnection("someConnectionString"))
    {
        conn.Open();
        pipeline.Process(conn.Query<SomeType>("SOME SQL HERE", buffered:true),cts.Token).ToList();
    }
    
    var pipeline=pipeline.Create(st=>
    {
    //和圣做点什么
    return someBool;//如果成功或失败,则返回someBool
    });
    var cts=新的CancellationTokenSource();
    //10秒后取消(只是为了好玩)
    TimeSpan.FromSeconds(10)).Subscribe(s=>cts.Cancel());
    使用(var conn=newsqlconnection(“someConnectionString”))
    {
    conn.Open();
    Process(conn.Query(“这里有些SQL”,缓冲:true),cts.Token.ToList();
    }
    
    选择此选项的原因是为了演示使用Dapper是多么容易,并行扩展Extras是多么强大和方便,但对于您的示例,它是故意设计过度的…:)我希望你能原谅我。需要结尾处的ToList(),否则不会对IEnumerable执行任何操作。或者,您可以使用以下方法:

    Console.WriteLine(
        pipeline.Process(conn.Query<SomeType>("SOME SQL HERE", buffered: true), cts.Token).All(b => b)
            ? "All records processed successfully"
            : "Some records failed");
    
    Console.WriteLine(
    Process(conn.Query(“这里有些SQL”,缓冲:true),cts.Token).All(b=>b)
    ?“已成功处理所有记录”
    :“某些记录失败”);
    
    如果要从数据处理步骤内部取消,请首先声明cts:

    var cts = new CancellationTokenSource();
    var pipeline = Pipeline.Create<SomeType,bool>(st =>
    {
        //Do something with st
        //you could even cancel from here
        if(someOtherBool)
            cts.Cancel();
        return someBool; //some bool if you succeeded or not for example
    });
    
    var cts=new CancellationTokenSource();
    var pipeline=pipeline.Create(st=>
    {
    //和圣做点什么
    //你甚至可以从这里取消
    如果(其他人)
    cts.Cancel();
    返回someBool;//例如,如果成功或失败,则返回someBool
    });
    
    如果不想声明特定类型:

    var cts = new CancellationTokenSource();
    var pipeline = Pipeline.Create<dynamic,bool>(d =>
    {
        //Do something with data
        if(someOtherBool)
            cts.Cancel();
        return someBool; //some bool if you succeeded or not
    });
    
    using (var conn = new SqlConnection("someConnectionString"))
    {
        conn.Open();
        foreach (var b in pipeline.Process(conn.Query("SOME SQL HERE", buffered: true), cts.Token))
        {
            Console.WriteLine(b?"Success":"Failure");
        }
    }
    
    var cts=new CancellationTokenSource();
    var pipeline=pipeline.Create(d=>
    {
    //处理数据
    如果(其他人)
    cts.Cancel();
    return someBool;//如果成功或失败,则返回someBool
    });
    使用(var conn=newsqlconnection(“someConnectionString”))
    {
    conn.Open();
    foreach(pipeline.Process(conn.Query(“这里有些SQL”,缓冲:true),cts.Token)中的var b)
    {
    控制台写入线(b?“成功”:“失败”);
    }
    }
    
    最后要提到的一点是,cts.Cancel()通常会在内部线程上引发异常,因此如果需要,请将管道封装在try/catch中

    更新2

    在阅读了作者的评论后,我仍然选择了简洁、小便和Rx(双关语)的组合

    var cts=new CancellationTokenSource();
    var pipeline=pipeline.Create(d=>
    {
    //对步骤1中的数据执行一些操作
    如果(某些条件检查)
    cts.Cancel();
    返回d;
    }).Next(d=>
    {
    //第二步是处理数据
    如果(某些条件检查)
    cts.Cancel();
    返回d;
    });
    订阅=可观察的时间间隔(TimeSpan.FromMinutes(1))。订阅(>
    {
    尝试
    {
    使用(var conn=newsqlconnection(“someConnectionString”))
    {
    conn.Open();
    foreach(pipeline.Process(conn.Query(“此处的一些SQL”,缓冲:true),cts.Token)中的var v)
    {
    //对结果做某事或忽略结果
    }
    }
    }
    捕获(聚合异常e)
    {
    //调查发生的情况,可能是处理中的错误
    //或手术取消
    }
    捕获(例外e)
    {
    //所有其他例外情况
    }
    });
    
    Rx让我创建了一个整洁的可观察的,每分钟都会开火。我也可以设计一个在上一次运行一段时间不活动后触发的,在这种情况下我更喜欢interval

    PEE让我创建一个整洁的工作流,在这里我可以指定对从数据库检索到的一个数据项执行的多个步骤。通过访问CancellationTokenSource,我可以在每个步骤完成后立即取消所有步骤,因此,如果一条记录在步骤1中,另一条记录在步骤N中,则这两条记录都将在各自的代码块完成后立即取消

    Dapper在与数据库交谈时只是一个时间服务器,句号

    然而,正如您所知,我并没有真正使用线程或任务,所以我在这里回答作者的问题吗?不是真的。相反,我为他提供了一个alterna
    var cts = new CancellationTokenSource();
    var pipeline = Pipeline.Create<dynamic,bool>(d =>
    {
        //Do something with data
        if(someOtherBool)
            cts.Cancel();
        return someBool; //some bool if you succeeded or not
    });
    
    using (var conn = new SqlConnection("someConnectionString"))
    {
        conn.Open();
        foreach (var b in pipeline.Process(conn.Query("SOME SQL HERE", buffered: true), cts.Token))
        {
            Console.WriteLine(b?"Success":"Failure");
        }
    }
    
    var cts = new CancellationTokenSource();
    var pipeline = Pipeline.Create<dynamic, dynamic>(d =>
    {
        //Do something with data in step 1
        if (someConditionalCheck)
            cts.Cancel();
        return d; 
    }).Next<dynamic>(d =>
    {
        //do something with data is step 2
        if(someConditionalCheck)
            cts.Cancel();
        return d;
    });
    
    subscription = Observable.Interval(TimeSpan.FromMinutes(1)).Subscribe(_ =>
    {
        try
        {
            using (var conn = new SqlConnection("someConnectionString"))
            {
                conn.Open();
                foreach (var v in pipeline.Process(conn.Query("SOME SQL HERE", buffered: true), cts.Token))
                {
                    //Do something with or ignore the result
                }
            }
        }
        catch (AggregateException e)
        {
            //Investigate what happened, could be error in processing 
            //or operation cancelled
        }
        catch (Exception e)
        {
            //All other exceptions
        }
    });