转载

http://eli.thegreenplace.net/2016/the-promises-and-challenges-of-stdasync-task-based-parallelism-in-c11/

One of the biggest and most impactful changes C++11 heralds is a standardized threading library, along with a documented memory model for the language. While extremely useful and obviating the dilemma of non-portable code vs. third-party libraries for threading, this first edition of the threading libraries is not without kinks. This article is a brief overview of how C++11 tries to enable a "task-based parallelism" idiom with the introduction of std::async, and the challenges it runs into.

Warning: this article is opinionated, especially its last third or so. I'll be happy to get corrections and suggestions in comments or email.

Background - threads vs. tasks

When I'm talking about "thread-based parallelism", I mean manual, low-level management of threads. Something like using pthreads or the Windows APIs for threads directly. You create threads, launch them, "join" them, etc. Even though threads are an OS abstraction, this is as close as you can get to the machine. In such cases, the programmer knows (or better know!) exactly how many threads he has running at any given time, and has to take care of load-balancing the work between them.

"Task-based parallelism" refers to a higher level of abstraction, where the programmer manages "tasks" - chunks of work that has to be done, while the library (or language) presents an API to launch these tasks. It is then the library's job to launch threads, make sure there are not too few or too many of them, make sure the work is reasonably load-balanced, and so on. For better or worse, this gives the programmer less low-level control over the system, but also higher-level, more convenient and safer APIs to work with. Some will claim that this also leads to better performance, though this really depends on the application.

Threads and tasks in C++11

The C++11 thread library gives us a whole toolbox for working at the thread level. We have std::thread along with a horde of synchronization and signaling mechanisms, a well-defined memory model, thread-local data and atomic operations right there in the standard.

C++11 also tries to provide a set of tools for task-based parallelism, revolving around std::async. It succeeds in some respects, and fails in others. I will go ahead and say in advance that I believe std::async is a very nice tool to replace direct std::thread usage on the low level. On the other hand, it is not really a good task-based parallelism abstraction. The rest of the article will cover these claims in detail.

Using std::async as a smarter std::thread

While it's great to have std::thread in standard C++, it's a fairly low level construct. As such, its usage is often more cumbersome than we'd want, and also more error-prone than we'd want. Therefore, an experienced programmer would sit down and come up with a slightly higher-level abstraction that makes C++ threading a bit more pleasant and also safer. The good news is that someone has already written this abstraction, and even made it standard. It's called std::async.

Here's a simple example of using a worker thread to perform some work - in this case add up integers in a vector [1]:

void accumulate_block_worker(int* data, size_t count, int* result) {
*result = std::accumulate(data, data + count, 0);
} void use_worker_in_std_thread() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
int result;
std::thread worker(accumulate_block_worker,
v.data(), v.size(), &result);
worker.join();
std::cout << "use_worker_in_std_thread computed " << result << "\n";
}

Straightforward enough. The thread is created and then immediately joined (waited upon to finish in a blocking manner). The result is communicated back to the caller via a pointer argument, since a std::thread cannot have a return value. This already points at a potential issue: when we write computation functions in C++ we usually employ the return value construct, rather than taking results by reference/pointer. Say we had a function already that did work, and was used in serial code, and we want to launch it in a std::thread. Since that function most likely returns its value, we'd need to either write a new version of it, or create some sort of wrapper.

Here's an alternative using std::async and std::future:

int accumulate_block_worker_ret(int* data, size_t count) {
return std::accumulate(data, data + count, 0);
} void use_worker_in_std_async() {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
std::future<int> fut = std::async(
std::launch::async, accumulate_block_worker_ret, v.data(), v.size());
std::cout << "use_worker_in_std_async computed " << fut.get() << "\n";
}

I'm passing the std::launch::async policy explicitly - more on this in the latter part of the article. The main thing to note here is that now the actual function launched in a thread is written in a natural way, returning the value it computed; no by-pointer output arguments in sight. std::async takes the return type of the function and returns it wrapped in a std::future, which is another handy abstraction. Read more about futures and promises in concurrent programming on Wikipedia. In the code above, the waiting for the computation thread to finish happens when we call get() on the future.

I like how the future decouples the task from the result. In more complex code, you can pass the future somewhere else, and it encapsulates both the thread to wait on and the result you'll end up with. The alternative of using std::thread directly is more cumbersome, because there are two things to pass around.

Here is a contrived example, where a function launches threads but then wants to delegate waiting for them and getting the results to some other function. It represents many realistic scenarios where we want to launch tasks in one place but collect results in some other place. First, a version with std::thread:

// Demonstrates how to launch two threads and return two results to the caller
// that will have to wait on those threads. Gives half the input vector to
// one thread, and the other half to another.
std::vector<std::thread>
launch_split_workers_with_std_thread(std::vector<int>& v,
std::vector<int>* results) {
std::vector<std::thread> threads;
threads.emplace_back(accumulate_block_worker, v.data(), v.size() / 2,
&((*results)[0]));
threads.emplace_back(accumulate_block_worker, v.data() + v.size() / 2,
v.size() / 2, &((*results)[1]));
return threads;
} ... {
// Usage
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int> results(2, 0);
std::vector<std::thread> threads =
launch_split_workers_with_std_thread(v, &results);
for (auto& t : threads) {
t.join();
}
std::cout << "results from launch_split_workers_with_std_thread: "
<< results[0] << " and " << results[1] << "\n";
}

Note how the thread objects have to be propagated back to the caller (so the caller can join them). Also, the result pointers have to be provided by the caller because otherwise they go out of scope [2].

Now, the same operation using std::async and futures:

using int_futures = std::vector<std::future<int>>;

int_futures launch_split_workers_with_std_async(std::vector<int>& v) {
int_futures futures;
futures.push_back(std::async(std::launch::async, accumulate_block_worker_ret,
v.data(), v.size() / 2));
futures.push_back(std::async(std::launch::async, accumulate_block_worker_ret,
v.data() + v.size() / 2, v.size() / 2));
return futures;
} ... {
// Usage
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
int_futures futures = launch_split_workers_with_std_async(v);
std::cout << "results from launch_split_workers_with_std_async: "
<< futures[0].get() << " and " << futures[1].get() << "\n";
}

Once again, the code is cleaner and more concise. Bundling the thread handle with the result it's expected to produce just makes more sense.

If we want to implement more complex result sharing schemes, things get even trickier. Say we want two different threads to wait on the computation result. You can't just call join on a thread from multiple other threads. Or at least, not easily. A thread that was already joined will throw an exception if another join is attempted. With futures, we have std::shared_future, which wraps a std::future and permits concurrent access from multiple threads that may want to get the future's result.

Setting a timeout on retrieving task results

Say we launched a thread to do a computation. At some point we'll have to wait for it to finish in order to obtain the result. The wait may be trivial if we set the program up in a certain way, but it can actually take time in some situations. Can we set a timeout on this wait so that we don't block for too long? With the pure std::threadsolution, it won't be easy. You can't set a timeout on the join() method, and other solutions are convoluted (such as setting up a "cooperative" timeout by sharing a condition variable with the launched thread).

With futures returned from std::async, nothing could be easier, since std::future has a wait_for() method that takes a timeout:

int accumulate_block_worker_ret(int* data, size_t count) {
std::this_thread::sleep_for(std::chrono::seconds(3));
return std::accumulate(data, data + count, 0);
} int main(int argc, const char** argv) {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
std::future<int> fut = std::async(
std::launch::async, accumulate_block_worker_ret, v.data(), v.size());
while (fut.wait_for(std::chrono::seconds(1)) != std::future_status::ready) {
std::cout << "... still not ready\n";
}
std::cout << "use_worker_in_std_async computed " << fut.get() << "\n"; return 0;
}

Propagating exceptions between threads

If you're writing C++ code with exceptions enabled, you are kinda "living on the edge". You always have to keep a mischievous imaginary friend on your left shoulder who will remind you that at any point in the program an exception can be thrown and then "how are you handling it?". Threads add another dimension to this (already difficult) problem. What happens when a function launched in a std::thread throws an exception?

void accumulate_block_worker(int* data, size_t count, int* result) {
throw std::runtime_error("something broke");
*result = std::accumulate(data, data + count, 0);
} ... {
// Usage.
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
int result;
std::thread worker(accumulate_block_worker,
v.data(), v.size(), &result);
worker.join();
std::cout << "use_worker_in_std_thread computed " << result << "\n";
}

This:

terminate called after throwing an instance of 'std::runtime_error'
what(): something broke
Aborted (core dumped)

Ah, silly me, I didn't catch the exception. Let's try this alternative usage:

try {
std::thread worker(accumulate_block_worker,
v.data(), v.size(), &result);
worker.join();
std::cout << "use_worker_in_std_thread computed " << result << "\n";
} catch (const std::runtime_error& error) {
std::cout << "caught an error: " << error.what() << "\n";
}

Nope:

terminate called after throwing an instance of 'std::runtime_error'
what(): something broke
Aborted (core dumped)

What's going on? Well, as the C++ standard clearly states, "~thread(), if joinable(), calls std::terminate()". So trying to catch the exception in another thread won't help.

While the example shown here is synthetic, there are many real-world cases where code executed in a thread can throw an exception. In regular, non-threaded call, we may reasonably expect that this exception should be handled somewhere higher up the call stack. If the code runs in a thread, however, this assumption is broken.

It means that we should wrap the function running in the new thread in additional code that will catch all exceptions and somehow transfer them to the calling thread. Yet another "result" to return, as if returning the actual result of the computation wasn't cumbersome enough.

Once again, std::async to the rescue! Let's try this again:

int accumulate_block_worker_ret(int* data, size_t count) {
throw std::runtime_error("something broke");
return std::accumulate(data, data + count, 0);
} ... {
// Usage.
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
try {
std::future<int> fut = std::async(
std::launch::async, accumulate_block_worker_ret, v.data(), v.size());
std::cout << "use_worker_in_std_async computed " << fut.get() << "\n";
} catch (const std::runtime_error& error) {
std::cout << "caught an error: " << error.what() << "\n";
}
}

Now we get:

caught an error: something broke

The exception was propagated to the calling thread through the std::future and re-thrown when its get()method is called.

This is also the place to mention that the C++11 thread library provides many low-level building blocks for implementing high-level threading and task constructs. Returning a std::future from std::async is a fairly high-level abstraction, tailored for a specific kind of task management. If you want to implement something more advanced, like a special kind of concurrent queue that manages tasks, you'll be happy to hear that tools likestd::promise and std::packaged_task are right there in the standard library to make your life more convenient. They let you associate functions with futures, and set exceptions separately from real results on those futures. I'll leave a deeper treatment of these topics to another day.

... but is this real task-based parallelism?

So we've seen how std::async helps us write robust threaded programs with smaller code compared to "raw" std::threads. If your threading needs are covered by std::async, you should definitely use it instead of toiling to re-implement the same niceties with raw threads and other low-level constructs. But does std::async enable real task-based parallelism, wherein you can nonchalantly hand it functions and expect it to load-distribute them for you over some existing thread pool to use OS resources efficiently? Unfortunately, no. Well, at least in the current version of the C++ standard, not yet.

There are many problems. Let's start with the launch policy.

In all the samples shown above, I'm explicitly passing the async policy to std::async to circumvent the issue. async is not the only policy it supports. The other one is deferred, and the default is actually async | deferred, meaning that we leave it to the runtime to decide. Except that we shouldn't.

The deferred policy means that the task will run lazily on the calling thread only when get() is called on the future it returns. This is dramatically different from the async policy in many respects, so just letting the runtime choose either sound like it may complicate programming. Consider the wait_for example I've shown above. Let's modify it to launch the accumulation task with a deferred policy:

int accumulate_block_worker_ret(int* data, size_t count) {
std::this_thread::sleep_for(std::chrono::seconds(3));
return std::accumulate(data, data + count, 0);
} int main(int argc, const char** argv) {
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
std::future<int> fut = std::async(
std::launch::deferred, accumulate_block_worker_ret, v.data(), v.size());
while (fut.wait_for(std::chrono::seconds(1)) != std::future_status::ready) {
std::cout << "... still not ready\n";
}
std::cout << "use_worker_in_std_async computed " << fut.get() << "\n"; return 0;
}

Running it:

$ ./using-std-future
... still not ready
... still not ready
... still not ready
... still not ready
... still not ready
... still not ready
... still not ready
^C

Oops, what's going on? The problem is that with the deferred policy, the call to wait_for on the future doesn't actually run the task. Only get() does. So we're stuck in an infinite loop. This can be fixed, of course (by also checking for a std::future_status::deferred status from wait_for()), but requires extra thinking and extra handling. It's not just a matter of not getting stuck in a loop, it's also a matter of what do we do in case the task is deferred? Handling both async and deferred tasks in the same caller code becomes tricky. When we use the default policy, we let the runtime decide when it wants to use deferred instead of async, so bugs like this may be difficult to find since they will only manifest occasionally under certain system loads.

Tasks and TLS

The C++11 standard also added TLS support with the thread_local keyword, which is great because TLS is a useful technique that hasn't been standardized so far. Let's try a synthetic example showing how it mixes with std::async's launch policices:

thread_local int tls_var;

int read_tls_var() {
return tls_var;
} int main(int argc, const char** argv) {
tls_var = 50; std::future<int> fut = std::async(std::launch::deferred, read_tls_var);
std::cout << "got from read_tls_var: " << fut.get() << "\n";
return 0;
}

When run, this shows the value 50, because read_tls_var runs in the calling thread. If we change the policy to std::launch::async, it will instead show 0. That's because read_tls_var now runs in a new thread where tls_varwasn't set to 50 by main. Now imagine the runtime decides if your task runs in the same thread or another thread. How useful are TLS variables in this scenario? Not very much, unfortunately. Well unless you love non-determinism and multi-threading Heisenbugs :-)

也就是说,

如果是std::launch::deferred是在当前线程执行,且是在调用future.get()的时候开始执行,

如果是std::launch::async是在一个新的线程执行

Tasks and mutexes

Here's another fun example, this time with mutexes. Consider this piece of code:

int task(std::recursive_mutex& m) {
m.lock();
return 42;
} int main(int argc, const char** argv) {
std::recursive_mutex m;
m.lock(); std::future<int> fut = std::async(std::launch::deferred, task, std::ref(m));
std::cout << "got from task: " << fut.get() << "\n";
return 0;
}

It runs and shows 42 because the same thread can lock a std::recursive_mutex multiple times. If we switch the launch policy to async, the program deadlocks because a different thread cannot lock a std::recursive_mutexwhile the calling thread is holding it. Contrived? Yes. Can this happen in real code - yes, of course. If you're thinking to yourself "he's cheating, what is this weird std::recursive_mutex example specifically tailored to show a problem...", I assure you that a regular std::mutex has its own problems. It has to be unlocked in the thread it was locked in. So if task unlocked a regular std::mutex that was locked by main instead, we'd also have an issue. Unlocking a mutex in a different thread is undefined behavior. With the default launch policy, this undefined behavior would happen just sometimes. Lovely.

Bartosz Milewski has some additional discussion of these problems here and also here. Note that they will haunt more advanced thread strategies as well. Thread pools reuse the same thread handles for different tasks, so they'll also have to face TLS and mutex thread-locality issues. Whatever the adopted solution ends up being, some additional constraints will have to be introduced to make sure it's not too easy to shoot yourself in the foot.

Is std::async fundamentally broken?

Due to the problems highlighted above, I'd consider the default launch policy of std::async broken and would never use it in production code. I'm not the only one thinking this way. Scott Meyers, in his "Effective Modern C++", recommends the following wrapper to launch tasks:

template <typename F, typename... Ts>
inline auto reallyAsync(F&& f, Ts&&... params) {
return std::async(std::launch::async, std::forward<F>(f),
std::forward<Ts>(params)...);
}

Use this instead of raw std::async calls to ensure that the tasks are always launched in fresh threads, so that we can reason about our program more deterministically.

The authors of gcc came to realize this as well, and switched the libstdc++ default launch policy tostd::launch::async in mid-2015. In fact, as the discussion in that bug highlights, std::async came close to being deprecated in the next C++ standard, since the standards committee realized it's not really possible to implement real task-based parallelism with it without non-deterministic and undefined behavior in some corner cases. And it's the role of the standards committee to ensure all corners are covered [3].

It's evident from online sources that std::async was a bit rushed into the C++11 standard, when the committee didn't have enough time to standardize a more comprehensive library solution such as thread pools. std::asyncwas put there as a compromise, as part of a collection of low-level building blocks that could be used to build higher-level abstractions later. But actually, it can't. Or at least not easily. "Real" task-based parallel systems feature things like task migration between threads, task stealing queues, etc. It will just keep hitting the problems highlighted above (TLS, mutexes, etc.) in real user code. A more comprehensive overhaul is required. Luckily, this is exactly what the standards commitee is toiling on - robust high-level concurrency primitives for the C++17 version of the standard.

Conclusion and practical advice

This article started by expounding the virtues of std::async compared to plain std::threads, but finished by pointing out numerous problems with std::async that one needs to be aware of. So, what do we do?

I actually think that by being careful to stay within the well-defined limits of std::async, we can enjoy its benefits without running into the gotchas. Specifically:

  1. Prefer std::async to std::thread. Futures are just too useful to ignore; especially if your code deals with exception handling, this is the only sane way to stay safe. Results provided by different threads should be wrapped in futures.
  2. Always use the std::launch::async policy with std::async if you actually want multi-threading. Do not rely on the default policy. Do not use deferred unless you have very special needs. Remember that deferred is just syntactic sugar over holding a function pointer to call it later.
  3. If you need a real thread pool or some other higher-level concurrency construct, use a library or roll your own. Standard objects like std::futurestd::promise and std::packaged_task can be very helpful.

[1]

Here and elsewhere, I'm trying to strip the code down to bare essentials, in order to demonstrate the actual threading concepts the article focuses on. C++ has a lot of complexities which I'm occasionally leaving behind, on purpose. For example the accumulator worker discussed here is not very generic or STL-y. Rewriting it to be templated and acting on iterators instead of pointer + size is left as an exercise for the diligent reader.

Full code samples for this post are available at https://github.com/eliben/code-for-blog/tree/master/2016/std-async

[2] Alternatively, launch_split_workers_with_std_thread could return a vector of thread/result pairs. However, multiple return values in C++ are messy no matter how you go at them, so it wouldn't result in much cleaner code. If you want to say "let's put them together in a class", then you're getting close to implementing std::future yourself :-)
[3]

To be completely fair, there's another problem with std::async that was the main driver for the call to deprecate it - the "waiting destructor" problem with the futures returned by std::async. There are many discussions online about this issue. A couple I recommend are this one by Scott Meyers and this SG1 paper by Nicolai Josuttis.

The gist of the issue is that a std::future returned by std::async will block in its destructor until the launched thread joins. While this behavior is important in order to ensure we don't have a runaway thread that accesses deallocated data, it also has its problems since some code may not like being blocked unexpectedly. And recall that a destructor is also called when an exception happens - another complication. In addition to the links above, also read this other article by Meyers to get a clearer understanding of the issue.

While the C++ standards committee came dangerously close to deprecating std::async for this reason, it seems that it has survived for now, with a proposal to have two different kinds of futures in the standard library, and changing std::async to return a waiting_future type, to mark this wait explicitly. In any case, be wary of this problem.


Comments

 

comments powered by Disqus

http://stackoverflow.com/questions/30810305/confusion-about-threads-launched-by-stdasync-with-stdlaunchasync-parameter

The std::async (part of the <future> header) function template is used to start a (possibly) asynchronous task. It returns a std::future object, which will eventually hold the return value of std::async's parameter function.

When the value is needed, we call get() on the std::future instance; this blocks the thread until the future is ready and then returns the value. std::launch::async or std::launch::deferred can be specified as the first parameter to std::async in order to specify how the task is run.

  1. std::launch::async indicates that the function call must be run on its own (new) thread. (Take user @T.C.'s comment into account).
  2. std::launch::deferred indicates that the function call is to be deferred until either wait() or get() is called on the future. Ownership of the future can be transferred to another thread before this happens.
  3. std::launch::async | std::launch::deferred indicates that the implementation may choose. This is the default option (when you don't specify one yourself). It can decide to run synchronously.

Is a new thread always launched in this case?

From 1., we can say that a new thread is always launched.

Are my assumptions [on std::launch::deferred] correct?

From 2., we can say that your assumptions are correct.

What is that supposed to mean? [in relation to a new thread being launched or not depending on the implementation]

From 3., as std::launch::async | std::launch::deferred is the default option, it means that the implementation of the template function std::async will decide whether it will create a new thread or not. This is because some implementations may be checking for over scheduling.

WARNING

The following section is not related to your question, but I think that it is important to keep in mind.

The C++ standard says that if a std::future holds the last reference to the shared state corresponding to a call to an asynchronous function, that std::future's destructor must block until the thread for the asynchronously running function finishes. An instance of std::future returned by std::async will thus block in its destructor.

void operation()
{
auto func = [] { std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); };
std::async( std::launch::async, func );
std::async( std::launch::async, func );
std::future<void> f{ std::async( std::launch::async, func ) };
}

This misleading code can make you think that the std::async calls are asynchronous, they are actually synchronous. The std::future instances returned by std::async are temporary and will block because their destructor is called right when std::async returns as they are not assigned to a variable.

The first call to std::async will block for 2 seconds, followed by another 2 seconds of blocking from the second call to std::async. We may think that the last call to std::async does not block, since we store its returned std::future instance in a variable, but since that is a local variable that is destroyed at the end of the scope, it will actually block for an additional 2 seconds at the end of the scope of the function, when local variable f is destroyed.

In other words, calling the operation() function will block whatever thread it is called on synchronously for approximately 6 seconds. Such requirements might not exist in a future version of the C++ standard.

Sources of information I used to compile these notes:

C++ Concurrency in Action: Practical Multithreading, Anthony Williams

Scott Meyers' blog post: http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html

The promises and challenges of std::async task-based parallelism in C++11 C++11 std::async/future/promise的更多相关文章

  1. C++ async task

    最近在搞Android 开发,里面多线程的使用比较频繁,java多线程接口很方便. Thread, AysncTask, Handler 这些接口比起posix提供的pthread_create()等 ...

  2. Await Async Task

    class Program { static void Main(string[] args) { Console.WriteLine("=======Start Main!======== ...

  3. Android菜鸟的成长笔记(13)——异步任务(Async Task)

    原文:[置顶] Android菜鸟的成长笔记(13)——异步任务(Async Task) Android的UI线程主要负责处理用户的事件及图形显示,因此主线程UI不能阻塞,否则会弹出一个ANR(App ...

  4. async/task/await

    async/task/await三组合是.NET Framework 4.5带给.NET开发者的大礼,合理地使用它,可以提高应用程序的吞吐能力. 但是它的使用有点绕人,如果不正确使用,会带来意想不到的 ...

  5. Rx与Async Task的简单对比

    有关Reactive Extensions的介绍可见https://rx.codeplex.com/,总的来说,你可以当它是又一个异步编程的框架,它以观察者模式实现了对数据流的的“订阅”.一个列表,一 ...

  6. async task 异步消息

     async 和 await 是用来定义的异步方法,async  关键字是上下文关键字,原因在于只有当它修饰方法.lambda 表达式或匿名方法时,它才是关键字. 在所有其他上下文中,都会将其解释为标 ...

  7. c# async Task await Result 死锁

    最近项目数据量较大,使用 async Task异步增加执行效率 遇到问题,当前有2个计算非常耗时,现在需要你优化一下,这2个计算并行执行,2个计算执行完成后将2个结果sum返回给用户 当前我是这样实现 ...

  8. 微信小程序中出现Invoking Page() in async task.问题

    在做项目中需要让页面跳到外网,用到了<web-view src=""> </web-view>组件,需要新建一个文件放这个组件,调接口的时候链接连到这个页面 ...

  9. 《C#并发编程经典实例》学习笔记—2.8 处理 async Task 方法的异常

    异常处理一直是所有编程语言不可避免需要考虑的问题,C#的异步方法的异常处理和同步方法并无差别,同样要借助 try catch 语句捕获异常. 首先编写一个抛出异常的方法 static async Ta ...

随机推荐

  1. 线程间操作无效: 从不是创建控件“textBox2”的线程访问它

    如何:对 Windows 窗体控件进行线程安全调用 线程间操作无效: 从不是创建控件的线程访问它的三种方法 如果使用多线程处理来提高 Windows 窗体应用程序的性能,则你必须确保以线程安全的方式调 ...

  2. SQLSEVER 中的那些键和约束

    SQL Server中有五种约束类型,分别是 PRIMARY KEY约束.FOREIGN KEY约束.UNIQUE约束.DEFAULT约束.和CHECK约束.查看或者创建约束都要使用到 Microso ...

  3. [BZOJ4025]二分图(线段树分治,并查集)

    4025: 二分图 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2191  Solved: 800[Submit][Status][Discuss] ...

  4. 【斜率优化】Average

    [UVa1451]Average 算法竞赛入门经典第8章8-9 ( P243 ) 题目大意:给定一个长度为N的01串,选择一个长度至少为L的连续子串,使序列平均值最大 (N<=100001) 题 ...

  5. 【博弈论】【SG函数】【线段树】Petrozavodsk Summer Training Camp 2016 Day 9: AtCoder Japanese Problems Selection, Thursday, September 1, 2016 Problem H. Cups and Beans

    一开始有n个杯子,每个杯子里有一些豆子,两个人轮流操作,每次只能将一个豆子移动到其所在杯子之前的某个杯子里,不过可以移动到的范围只有一段区间.问你是否先手必胜. 一个杯子里的豆子全都等价的,因为sg函 ...

  6. 【带权并查集】【离散化】vijos P1112 小胖的奇偶

    每个区间拆成r和l-1两个端点,若之内有偶数个1,则这两个端点对应的前缀的奇偶性必须相同,否则必须相反. 于是可以用带权并查集维护,每个结点储存其与其父节点的奇偶性是否相同,并且在路径压缩以及Unio ...

  7. [POI2010]Divine Divisor

    [POI2010]Divine Divisor 题目大意: 给你\(m(m\le600)\)个数\(a_i(a_i\le10^{18})\).\(n=\prod a_i\).现在要你找到一个最大的\( ...

  8. RMI(Remote Method Invocation ) 概念恢复

    1.RMI是远程方法调用的简称,像其名称暗示的那样,它能够帮助我们查找并执行远程对象,通俗的说,远程调用就像一个class放在A机器上,然后在B机器中调用这个class的方法. 2.EMI术语 在研究 ...

  9. Activit(活动)实践--知晓当前活动

    实际上,我们可能用的不是自己写的项目,而是从别人那里接手过来的代码,因为你刚进公司就有一个新项目开始的概率十分低.阅读别人代码时会有一个很头疼的问题,就是当你需要在某个界面上修改一些非常简单的东西时, ...

  10. Semaphore(信号量)源码分析

    1. Semaphore Semaphore和ReentrantReadWriteLock.ReadLock(读锁)都采用AbstractOwnableSynchronizer共享排队的方式实现. 关 ...