Yes, you should make a single connection to the cluster at startup and reuse it. ClusterHelper is a great way to do this.
Unfortunately, there isn't a great way to tell if it's initialized or not. You can call GetBucket() and trap the InitializationException, but this seems a big messy. I agree having an easy way to test this would be nice, I filed an issue to track this idea, https://issues.couchbase.com/browse/NCBC-1389. In the meantime, I'd recommend keeping your own boolean to track if you initialized or not.
Yes, buckets should definitely be left open for the lifetime of the cluster. While they implement Dispose(), you shouldn't call it. Instead, just use ClusterHelper.Close when you're done with your app, it cleans up the buckets for you. Buckets are reentrant, so you can use them simultaneously in as many threads as you like. Note, however, that priority to SDK 2.4 you might need to increase the size of your connection pool if you have lots of threads accessing them simultaneously. After SDK 2.4 the connection is multiplexed so it shouldn't be an issue.